@packtrack/layout 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.built/district.d.ts +2 -0
- package/.built/district.js +4 -3
- package/.built/index.d.ts +3 -0
- package/.built/index.js +3 -0
- package/.built/layout.d.ts +6 -0
- package/.built/layout.js +40 -11
- package/.built/monitor.d.ts +9 -0
- package/.built/monitor.js +15 -0
- package/.built/position.d.ts +3 -1
- package/.built/position.js +16 -0
- package/.built/positioner/point.d.ts +1 -1
- package/.built/span.d.ts +12 -0
- package/.built/span.js +77 -0
- package/.built/throttle.d.ts +9 -0
- package/.built/throttle.js +15 -0
- package/package.json +1 -1
- package/source/district.ts +21 -18
- package/source/index.ts +3 -0
- package/source/layout.ts +51 -11
- package/source/monitor.ts +19 -0
- package/source/position.ts +52 -32
- package/source/span.ts +88 -0
- package/source/throttle.ts +19 -0
package/.built/district.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Layout } from "./layout";
|
|
2
|
+
import { Monitor } from "./monitor";
|
|
2
3
|
import { PowerDistrict } from "./power-district";
|
|
3
4
|
import { Router } from "./router";
|
|
4
5
|
import { Section } from "./section";
|
|
@@ -9,6 +10,7 @@ export declare class District {
|
|
|
9
10
|
powerDistricts: PowerDistrict[];
|
|
10
11
|
sections: Section[];
|
|
11
12
|
routers: Router[];
|
|
13
|
+
monitors: Monitor[];
|
|
12
14
|
constructor(name: string, parent: District | Layout);
|
|
13
15
|
get domainName(): string;
|
|
14
16
|
dump(): void;
|
package/.built/district.js
CHANGED
|
@@ -6,6 +6,7 @@ export class District {
|
|
|
6
6
|
powerDistricts = [];
|
|
7
7
|
sections = [];
|
|
8
8
|
routers = [];
|
|
9
|
+
monitors = [];
|
|
9
10
|
constructor(name, parent) {
|
|
10
11
|
this.name = name;
|
|
11
12
|
this.parent = parent;
|
|
@@ -48,10 +49,10 @@ export class District {
|
|
|
48
49
|
return `
|
|
49
50
|
subgraph ${this.toDotReference()} {
|
|
50
51
|
label = ${JSON.stringify(this.name)}
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
${this.sections.map(section => section.toDotDefinition()).join('')}
|
|
53
54
|
${this.routers.map(router => router.toDotDefinition()).join('')}
|
|
54
|
-
|
|
55
|
+
|
|
55
56
|
${this.children.map(child => child.toDotDefinition()).join('')}
|
|
56
57
|
}
|
|
57
58
|
`;
|
|
@@ -60,7 +61,7 @@ export class District {
|
|
|
60
61
|
return `
|
|
61
62
|
${this.sections.map(section => section.toDotConnection()).join('')}
|
|
62
63
|
${this.routers.map(router => router.toDotConnection()).join('')}
|
|
63
|
-
|
|
64
|
+
|
|
64
65
|
${this.children.map(child => child.toDotConnection()).join('')}
|
|
65
66
|
`;
|
|
66
67
|
}
|
package/.built/index.d.ts
CHANGED
|
@@ -4,8 +4,11 @@ export * from './position';
|
|
|
4
4
|
export * from './route';
|
|
5
5
|
export * from './router';
|
|
6
6
|
export * from './section';
|
|
7
|
+
export * from './span';
|
|
7
8
|
export * from './tile';
|
|
8
9
|
export * from './track';
|
|
10
|
+
export * from './monitor';
|
|
11
|
+
export * from './throttle';
|
|
9
12
|
export * from './power-district/index';
|
|
10
13
|
export * from './power-district/activator';
|
|
11
14
|
export * from './power-district/monitor';
|
package/.built/index.js
CHANGED
|
@@ -4,8 +4,11 @@ export * from './position';
|
|
|
4
4
|
export * from './route';
|
|
5
5
|
export * from './router';
|
|
6
6
|
export * from './section';
|
|
7
|
+
export * from './span';
|
|
7
8
|
export * from './tile';
|
|
8
9
|
export * from './track';
|
|
10
|
+
export * from './monitor';
|
|
11
|
+
export * from './throttle';
|
|
9
12
|
export * from './power-district/index';
|
|
10
13
|
export * from './power-district/activator';
|
|
11
14
|
export * from './power-district/monitor';
|
package/.built/layout.d.ts
CHANGED
|
@@ -5,13 +5,19 @@ import { Section } from "./section";
|
|
|
5
5
|
import { Device } from "./device/index";
|
|
6
6
|
import { ResponderType } from "./positioner/responder-type";
|
|
7
7
|
import { Channel } from "./device/channel";
|
|
8
|
+
import { Monitor } from "./monitor";
|
|
9
|
+
import { Throttle } from "./throttle";
|
|
8
10
|
export declare class Layout {
|
|
9
11
|
name: string;
|
|
10
12
|
districts: District[];
|
|
11
13
|
devices: Device[];
|
|
12
14
|
responderType: ResponderType[];
|
|
15
|
+
monitors: Monitor[];
|
|
16
|
+
throttles: Throttle[];
|
|
13
17
|
get allDistricts(): District[];
|
|
14
18
|
static from(document: any): Layout;
|
|
19
|
+
loadMonitor(source: any, parent: District | Layout): Monitor;
|
|
20
|
+
loadThrottle(source: any, parent: District | Layout): Throttle;
|
|
15
21
|
loadDistrict(source: any, parent: District | Layout): District;
|
|
16
22
|
linkDistrict(source: any, district: District): void;
|
|
17
23
|
loadSection(source: any, district: District): void;
|
package/.built/layout.js
CHANGED
|
@@ -12,11 +12,15 @@ import { PointPositioner } from "./positioner/point";
|
|
|
12
12
|
import { PowerDistrictActivator } from "./power-district/activator";
|
|
13
13
|
import { PowerDistrictReverser } from "./power-district/reverser";
|
|
14
14
|
import { PowerDistrictMonitor } from "./power-district/monitor";
|
|
15
|
+
import { Monitor } from "./monitor";
|
|
16
|
+
import { Throttle } from "./throttle";
|
|
15
17
|
export class Layout {
|
|
16
18
|
name;
|
|
17
19
|
districts = [];
|
|
18
20
|
devices = [];
|
|
19
21
|
responderType = [];
|
|
22
|
+
monitors = [];
|
|
23
|
+
throttles = [];
|
|
20
24
|
get allDistricts() {
|
|
21
25
|
const districts = [];
|
|
22
26
|
function walkDistrict(district) {
|
|
@@ -36,28 +40,42 @@ export class Layout {
|
|
|
36
40
|
layout.name = railway.getAttribute('name');
|
|
37
41
|
const version = railway.getAttribute('version');
|
|
38
42
|
if (version == '1') {
|
|
39
|
-
let
|
|
40
|
-
while (
|
|
41
|
-
if (
|
|
42
|
-
layout.districts.push(layout.loadDistrict(
|
|
43
|
+
let child = railway.firstChild;
|
|
44
|
+
while (child) {
|
|
45
|
+
if (child.tagName == 'district') {
|
|
46
|
+
layout.districts.push(layout.loadDistrict(child, layout));
|
|
43
47
|
}
|
|
44
|
-
|
|
48
|
+
if (child.tagName == 'monitor') {
|
|
49
|
+
layout.monitors.push(layout.loadMonitor(child, layout));
|
|
50
|
+
}
|
|
51
|
+
if (child.tagName == 'throttle') {
|
|
52
|
+
layout.throttles.push(layout.loadThrottle(child, layout));
|
|
53
|
+
}
|
|
54
|
+
child = child.nextSibling;
|
|
45
55
|
}
|
|
46
|
-
|
|
56
|
+
child = railway.firstChild;
|
|
47
57
|
let index = 0;
|
|
48
|
-
while (
|
|
49
|
-
if (
|
|
50
|
-
layout.linkDistrict(
|
|
58
|
+
while (child) {
|
|
59
|
+
if (child.tagName == 'district') {
|
|
60
|
+
layout.linkDistrict(child, layout.districts[index]);
|
|
51
61
|
index++;
|
|
52
62
|
}
|
|
53
|
-
|
|
63
|
+
child = child.nextSibling;
|
|
54
64
|
}
|
|
55
65
|
}
|
|
56
66
|
else {
|
|
57
|
-
throw new Error(`
|
|
67
|
+
throw new Error(`Unsupported railway definition file version '${version}'`);
|
|
58
68
|
}
|
|
59
69
|
return layout;
|
|
60
70
|
}
|
|
71
|
+
loadMonitor(source, parent) {
|
|
72
|
+
const montior = new Monitor(this.findDevice(source.getAttribute('device')), parent);
|
|
73
|
+
return montior;
|
|
74
|
+
}
|
|
75
|
+
loadThrottle(source, parent) {
|
|
76
|
+
const throttle = new Throttle(this.findDevice(source.getAttribute('device')), parent);
|
|
77
|
+
return throttle;
|
|
78
|
+
}
|
|
61
79
|
loadDistrict(source, parent) {
|
|
62
80
|
const district = new District(source.getAttribute('name'), parent);
|
|
63
81
|
let child = source.firstChild;
|
|
@@ -80,6 +98,9 @@ export class Layout {
|
|
|
80
98
|
if (child.tagName == 'district') {
|
|
81
99
|
district.children.push(this.loadDistrict(child, district));
|
|
82
100
|
}
|
|
101
|
+
if (child.tagName == 'monitor') {
|
|
102
|
+
district.monitors.push(this.loadMonitor(child, district));
|
|
103
|
+
}
|
|
83
104
|
child = child.nextSibling;
|
|
84
105
|
}
|
|
85
106
|
return district;
|
|
@@ -220,6 +241,7 @@ export class Layout {
|
|
|
220
241
|
}
|
|
221
242
|
linkRouter(source, router) {
|
|
222
243
|
let child = source.firstChild;
|
|
244
|
+
let active;
|
|
223
245
|
while (child) {
|
|
224
246
|
if (child.tagName == 'route') {
|
|
225
247
|
const route = new Route(child.getAttribute('name'), router);
|
|
@@ -227,10 +249,17 @@ export class Layout {
|
|
|
227
249
|
route.in.out = router;
|
|
228
250
|
route.out = this.findSection(child.getAttribute('out'), router.district);
|
|
229
251
|
route.out.in = router;
|
|
252
|
+
if (child.hasAttribute('active')) {
|
|
253
|
+
if (active) {
|
|
254
|
+
throw new Error(`Router '${router.domainName}' has multiple active routes (${active.name}, ${route.name}).`);
|
|
255
|
+
}
|
|
256
|
+
active = route;
|
|
257
|
+
}
|
|
230
258
|
router.routes.push(route);
|
|
231
259
|
}
|
|
232
260
|
child = child.nextSibling;
|
|
233
261
|
}
|
|
262
|
+
router.activeRoute = active;
|
|
234
263
|
}
|
|
235
264
|
loadPowerDistrict(source, district) {
|
|
236
265
|
const powerDistrict = new PowerDistrict(source.getAttribute('name'), district);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Device } from "./device";
|
|
2
|
+
import { District } from "./district";
|
|
3
|
+
import { Layout } from "./layout";
|
|
4
|
+
export declare class Monitor {
|
|
5
|
+
device: Device;
|
|
6
|
+
scope: District | Layout;
|
|
7
|
+
constructor(device: Device, scope: District | Layout);
|
|
8
|
+
dump(): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Layout } from "./layout";
|
|
2
|
+
export class Monitor {
|
|
3
|
+
device;
|
|
4
|
+
scope;
|
|
5
|
+
constructor(device, scope) {
|
|
6
|
+
this.device = device;
|
|
7
|
+
this.scope = scope;
|
|
8
|
+
}
|
|
9
|
+
dump() {
|
|
10
|
+
console.group('Monitor');
|
|
11
|
+
console.log('scope:', this.scope instanceof Layout ? '*' : this.scope.domainName);
|
|
12
|
+
this.device.dump();
|
|
13
|
+
console.groupEnd();
|
|
14
|
+
}
|
|
15
|
+
}
|
package/.built/position.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export declare class SectionPosition {
|
|
|
5
5
|
reversed: boolean;
|
|
6
6
|
constructor(section: Section, offset: number, reversed: boolean);
|
|
7
7
|
get absolutePosition(): number;
|
|
8
|
-
advance(distance: number):
|
|
8
|
+
advance(distance: number): SectionPosition;
|
|
9
|
+
private invert;
|
|
9
10
|
toString(): string;
|
|
11
|
+
toPackTrackValue(): string;
|
|
10
12
|
}
|
package/.built/position.js
CHANGED
|
@@ -6,14 +6,23 @@ export class SectionPosition {
|
|
|
6
6
|
this.section = section;
|
|
7
7
|
this.offset = offset;
|
|
8
8
|
this.reversed = reversed;
|
|
9
|
+
if (offset > section.length || offset < 0) {
|
|
10
|
+
throw new Error(`Offset ${offset} out of range for section '${section.domainName}' (0 - ${section.length})`);
|
|
11
|
+
}
|
|
9
12
|
}
|
|
13
|
+
// returns the absolute position of the point inside the section
|
|
14
|
+
// regardless of direction
|
|
10
15
|
get absolutePosition() {
|
|
11
16
|
if (this.reversed) {
|
|
12
17
|
return this.section.length - this.offset;
|
|
13
18
|
}
|
|
14
19
|
return this.offset;
|
|
15
20
|
}
|
|
21
|
+
// TODO verify reverse
|
|
16
22
|
advance(distance) {
|
|
23
|
+
if (distance < 0) {
|
|
24
|
+
return this.invert().advance(-distance).invert();
|
|
25
|
+
}
|
|
17
26
|
if (this.offset + distance > this.section.length) {
|
|
18
27
|
const next = this.section.next(this.reversed);
|
|
19
28
|
if (!next) {
|
|
@@ -23,7 +32,14 @@ export class SectionPosition {
|
|
|
23
32
|
}
|
|
24
33
|
return new SectionPosition(this.section, this.offset + distance, this.reversed);
|
|
25
34
|
}
|
|
35
|
+
// reverse direction
|
|
36
|
+
invert() {
|
|
37
|
+
return new SectionPosition(this.section, this.section.length - this.offset, !this.reversed);
|
|
38
|
+
}
|
|
26
39
|
toString() {
|
|
27
40
|
return `${this.section.name} @ ${this.offset.toFixed(1)} ${this.reversed ? 'backward' : 'forward'}`;
|
|
28
41
|
}
|
|
42
|
+
toPackTrackValue() {
|
|
43
|
+
return `${this.section.domainName}@${this.offset}${this.reversed ? 'R' : 'F'}`;
|
|
44
|
+
}
|
|
29
45
|
}
|
|
@@ -8,6 +8,6 @@ export declare class PointPositioner extends Positioner {
|
|
|
8
8
|
channel: Channel;
|
|
9
9
|
responder: ResponderType;
|
|
10
10
|
constructor(track: Track, offset: number, channel: Channel, responder: ResponderType);
|
|
11
|
-
get position():
|
|
11
|
+
get position(): import("..").SectionPosition;
|
|
12
12
|
dump(): void;
|
|
13
13
|
}
|
package/.built/span.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SectionPosition } from "./position";
|
|
2
|
+
import { Section } from "./section";
|
|
3
|
+
export declare class Span {
|
|
4
|
+
head: SectionPosition;
|
|
5
|
+
inside: Section[];
|
|
6
|
+
tail: SectionPosition;
|
|
7
|
+
constructor(head: SectionPosition, inside: Section[], tail: SectionPosition);
|
|
8
|
+
contains(position: SectionPosition): boolean;
|
|
9
|
+
overlap(peer: Span): boolean;
|
|
10
|
+
get length(): number;
|
|
11
|
+
static trail(start: SectionPosition, end: SectionPosition): Span;
|
|
12
|
+
}
|
package/.built/span.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export class Span {
|
|
2
|
+
head;
|
|
3
|
+
inside;
|
|
4
|
+
tail;
|
|
5
|
+
constructor(head, inside, tail) {
|
|
6
|
+
this.head = head;
|
|
7
|
+
this.inside = inside;
|
|
8
|
+
this.tail = tail;
|
|
9
|
+
}
|
|
10
|
+
// TODO verify reverse
|
|
11
|
+
contains(position) {
|
|
12
|
+
if (this.inside.includes(position.section)) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (position.section == this.head.section) {
|
|
16
|
+
if (this.head.reversed) {
|
|
17
|
+
if (this.head.absolutePosition > position.absolutePosition) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
if (this.head.offset < position.absolutePosition) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (position.section == this.tail.section) {
|
|
28
|
+
if (this.tail.reversed) {
|
|
29
|
+
if (this.tail.absolutePosition < position.absolutePosition) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (this.tail.offset > position.absolutePosition) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
overlap(peer) {
|
|
42
|
+
return this.contains(peer.head) || this.contains(peer.tail);
|
|
43
|
+
}
|
|
44
|
+
get length() {
|
|
45
|
+
let length = 0;
|
|
46
|
+
if (this.head.reversed) {
|
|
47
|
+
length += this.head.section.length - this.head.offset;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
length += this.head.offset;
|
|
51
|
+
}
|
|
52
|
+
for (let section of this.inside) {
|
|
53
|
+
length += section.length;
|
|
54
|
+
}
|
|
55
|
+
if (this.tail.reversed) {
|
|
56
|
+
length += this.tail.section.length - this.tail.offset;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
length += this.tail.offset;
|
|
60
|
+
}
|
|
61
|
+
return length;
|
|
62
|
+
}
|
|
63
|
+
// TODO verify reverse
|
|
64
|
+
// TODO add efficient algo
|
|
65
|
+
static trail(start, end) {
|
|
66
|
+
let head = start;
|
|
67
|
+
const sections = [];
|
|
68
|
+
const increment = 1;
|
|
69
|
+
while (head.section != end.section) {
|
|
70
|
+
head = head.advance(increment);
|
|
71
|
+
if (head.section != start.section && head.section != end.section && !sections.includes(head.section)) {
|
|
72
|
+
sections.push(head.section);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return new Span(start, sections, end);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Device } from "./device";
|
|
2
|
+
import { District } from "./district";
|
|
3
|
+
import { Layout } from "./layout";
|
|
4
|
+
export declare class Throttle {
|
|
5
|
+
device: Device;
|
|
6
|
+
scope: District | Layout;
|
|
7
|
+
constructor(device: Device, scope: District | Layout);
|
|
8
|
+
dump(): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Layout } from "./layout";
|
|
2
|
+
export class Throttle {
|
|
3
|
+
device;
|
|
4
|
+
scope;
|
|
5
|
+
constructor(device, scope) {
|
|
6
|
+
this.device = device;
|
|
7
|
+
this.scope = scope;
|
|
8
|
+
}
|
|
9
|
+
dump() {
|
|
10
|
+
console.group('Throttle');
|
|
11
|
+
console.log('scope:', this.scope instanceof Layout ? '*' : this.scope.domainName);
|
|
12
|
+
this.device.dump();
|
|
13
|
+
console.groupEnd();
|
|
14
|
+
}
|
|
15
|
+
}
|
package/package.json
CHANGED
package/source/district.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Layout } from "./layout";
|
|
2
|
+
import { Monitor } from "./monitor";
|
|
2
3
|
import { PowerDistrict } from "./power-district";
|
|
3
4
|
import { Router } from "./router";
|
|
4
5
|
import { Section } from "./section";
|
|
5
6
|
|
|
6
7
|
export class District {
|
|
7
8
|
children: District[] = [];
|
|
8
|
-
|
|
9
|
+
|
|
9
10
|
powerDistricts: PowerDistrict[] = [];
|
|
10
11
|
sections: Section[] = [];
|
|
11
12
|
routers: Router[] = [];
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
monitors: Monitor[] = [];
|
|
15
|
+
|
|
13
16
|
constructor(
|
|
14
17
|
public name: string,
|
|
15
18
|
public parent: District | Layout
|
|
@@ -22,65 +25,65 @@ export class District {
|
|
|
22
25
|
|
|
23
26
|
return `${this.name}.${this.parent.domainName}`;
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
dump() {
|
|
27
30
|
console.group(`District ${this.domainName}`);
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
if (this.powerDistricts.length) {
|
|
30
33
|
console.group('power districts');
|
|
31
|
-
|
|
34
|
+
|
|
32
35
|
for (let district of this.powerDistricts) {
|
|
33
36
|
district.dump();
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
console.groupEnd();
|
|
37
40
|
}
|
|
38
|
-
|
|
41
|
+
|
|
39
42
|
if (this.sections.length) {
|
|
40
43
|
console.group('sections');
|
|
41
|
-
|
|
44
|
+
|
|
42
45
|
for (let section of this.sections) {
|
|
43
46
|
section.dump();
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
console.groupEnd();
|
|
47
50
|
}
|
|
48
|
-
|
|
51
|
+
|
|
49
52
|
if (this.children.length) {
|
|
50
53
|
console.group('children');
|
|
51
|
-
|
|
54
|
+
|
|
52
55
|
for (let district of this.children) {
|
|
53
56
|
district.dump();
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
console.groupEnd();
|
|
57
60
|
}
|
|
58
|
-
|
|
61
|
+
|
|
59
62
|
console.groupEnd();
|
|
60
63
|
}
|
|
61
|
-
|
|
64
|
+
|
|
62
65
|
toDotReference() {
|
|
63
66
|
return `cluster_${this.name.replace(/-/g, '_')}${this.parent instanceof District ? this.parent.toDotReference() : ''}`;
|
|
64
67
|
}
|
|
65
|
-
|
|
68
|
+
|
|
66
69
|
toDotDefinition() {
|
|
67
70
|
return `
|
|
68
71
|
subgraph ${this.toDotReference()} {
|
|
69
72
|
label = ${JSON.stringify(this.name)}
|
|
70
|
-
|
|
73
|
+
|
|
71
74
|
${this.sections.map(section => section.toDotDefinition()).join('')}
|
|
72
75
|
${this.routers.map(router => router.toDotDefinition()).join('')}
|
|
73
|
-
|
|
76
|
+
|
|
74
77
|
${this.children.map(child => child.toDotDefinition()).join('')}
|
|
75
78
|
}
|
|
76
79
|
`;
|
|
77
80
|
}
|
|
78
|
-
|
|
81
|
+
|
|
79
82
|
toDotConnection() {
|
|
80
83
|
return `
|
|
81
84
|
${this.sections.map(section => section.toDotConnection()).join('')}
|
|
82
85
|
${this.routers.map(router => router.toDotConnection()).join('')}
|
|
83
|
-
|
|
86
|
+
|
|
84
87
|
${this.children.map(child => child.toDotConnection()).join('')}
|
|
85
88
|
`;
|
|
86
89
|
}
|
|
@@ -97,8 +100,8 @@ export class District {
|
|
|
97
100
|
|
|
98
101
|
findSVGPositions() {
|
|
99
102
|
return [
|
|
100
|
-
...this.sections.map(section => section.findSVGPositions()),
|
|
103
|
+
...this.sections.map(section => section.findSVGPositions()),
|
|
101
104
|
...this.children.map(child => child.findSVGPositions())
|
|
102
105
|
];
|
|
103
106
|
}
|
|
104
|
-
}
|
|
107
|
+
}
|
package/source/index.ts
CHANGED
|
@@ -4,8 +4,11 @@ export * from './position';
|
|
|
4
4
|
export * from './route';
|
|
5
5
|
export * from './router';
|
|
6
6
|
export * from './section';
|
|
7
|
+
export * from './span';
|
|
7
8
|
export * from './tile';
|
|
8
9
|
export * from './track';
|
|
10
|
+
export * from './monitor';
|
|
11
|
+
export * from './throttle';
|
|
9
12
|
|
|
10
13
|
export * from './power-district/index';
|
|
11
14
|
export * from './power-district/activator';
|
package/source/layout.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { PointPositioner } from "./positioner/point";
|
|
|
12
12
|
import { PowerDistrictActivator } from "./power-district/activator";
|
|
13
13
|
import { PowerDistrictReverser } from "./power-district/reverser";
|
|
14
14
|
import { PowerDistrictMonitor } from "./power-district/monitor";
|
|
15
|
+
import { Monitor } from "./monitor";
|
|
16
|
+
import { Throttle } from "./throttle";
|
|
15
17
|
|
|
16
18
|
export class Layout {
|
|
17
19
|
name: string;
|
|
@@ -21,6 +23,9 @@ export class Layout {
|
|
|
21
23
|
devices: Device[] = [];
|
|
22
24
|
responderType: ResponderType[] = [];
|
|
23
25
|
|
|
26
|
+
monitors: Monitor[] = [];
|
|
27
|
+
throttles: Throttle[] = [];
|
|
28
|
+
|
|
24
29
|
get allDistricts() {
|
|
25
30
|
const districts: District[] = [];
|
|
26
31
|
|
|
@@ -48,35 +53,55 @@ export class Layout {
|
|
|
48
53
|
const version = railway.getAttribute('version');
|
|
49
54
|
|
|
50
55
|
if (version == '1') {
|
|
51
|
-
let
|
|
56
|
+
let child = railway.firstChild;
|
|
57
|
+
|
|
58
|
+
while (child) {
|
|
59
|
+
if (child.tagName == 'district') {
|
|
60
|
+
layout.districts.push(layout.loadDistrict(child, layout));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (child.tagName == 'monitor') {
|
|
64
|
+
layout.monitors.push(layout.loadMonitor(child, layout));
|
|
65
|
+
}
|
|
52
66
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
layout.districts.push(layout.loadDistrict(district, layout));
|
|
67
|
+
if (child.tagName == 'throttle') {
|
|
68
|
+
layout.throttles.push(layout.loadThrottle(child, layout));
|
|
56
69
|
}
|
|
57
70
|
|
|
58
|
-
|
|
71
|
+
child = child.nextSibling;
|
|
59
72
|
}
|
|
60
73
|
|
|
61
|
-
|
|
74
|
+
child = railway.firstChild;
|
|
62
75
|
let index = 0;
|
|
63
76
|
|
|
64
|
-
while (
|
|
65
|
-
if (
|
|
66
|
-
layout.linkDistrict(
|
|
77
|
+
while (child) {
|
|
78
|
+
if (child.tagName == 'district') {
|
|
79
|
+
layout.linkDistrict(child, layout.districts[index]);
|
|
67
80
|
|
|
68
81
|
index++;
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
|
|
84
|
+
child = child.nextSibling;
|
|
72
85
|
}
|
|
73
86
|
} else {
|
|
74
|
-
throw new Error(`
|
|
87
|
+
throw new Error(`Unsupported railway definition file version '${version}'`);
|
|
75
88
|
}
|
|
76
89
|
|
|
77
90
|
return layout;
|
|
78
91
|
}
|
|
79
92
|
|
|
93
|
+
loadMonitor(source, parent: District | Layout) {
|
|
94
|
+
const montior = new Monitor(this.findDevice(source.getAttribute('device')), parent);
|
|
95
|
+
|
|
96
|
+
return montior;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
loadThrottle(source, parent: District | Layout) {
|
|
100
|
+
const throttle = new Throttle(this.findDevice(source.getAttribute('device')), parent);
|
|
101
|
+
|
|
102
|
+
return throttle;
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
loadDistrict(source, parent: District | Layout) {
|
|
81
106
|
const district = new District(source.getAttribute('name'), parent);
|
|
82
107
|
|
|
@@ -107,6 +132,10 @@ export class Layout {
|
|
|
107
132
|
district.children.push(this.loadDistrict(child, district));
|
|
108
133
|
}
|
|
109
134
|
|
|
135
|
+
if (child.tagName == 'monitor') {
|
|
136
|
+
district.monitors.push(this.loadMonitor(child, district));
|
|
137
|
+
}
|
|
138
|
+
|
|
110
139
|
child = child.nextSibling;
|
|
111
140
|
}
|
|
112
141
|
|
|
@@ -314,6 +343,7 @@ export class Layout {
|
|
|
314
343
|
|
|
315
344
|
linkRouter(source, router: Router) {
|
|
316
345
|
let child = source.firstChild;
|
|
346
|
+
let active: Route;
|
|
317
347
|
|
|
318
348
|
while (child) {
|
|
319
349
|
if (child.tagName == 'route') {
|
|
@@ -325,11 +355,21 @@ export class Layout {
|
|
|
325
355
|
route.out = this.findSection(child.getAttribute('out'), router.district);
|
|
326
356
|
route.out.in = router;
|
|
327
357
|
|
|
358
|
+
if (child.hasAttribute('active')) {
|
|
359
|
+
if (active) {
|
|
360
|
+
throw new Error(`Router '${router.domainName}' has multiple active routes (${active.name}, ${route.name}).`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
active = route;
|
|
364
|
+
}
|
|
365
|
+
|
|
328
366
|
router.routes.push(route);
|
|
329
367
|
}
|
|
330
368
|
|
|
331
369
|
child = child.nextSibling;
|
|
332
370
|
}
|
|
371
|
+
|
|
372
|
+
router.activeRoute = active;
|
|
333
373
|
}
|
|
334
374
|
|
|
335
375
|
loadPowerDistrict(source, district: District) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Device } from "./device";
|
|
2
|
+
import { District } from "./district";
|
|
3
|
+
import { Layout } from "./layout";
|
|
4
|
+
|
|
5
|
+
export class Monitor {
|
|
6
|
+
constructor(
|
|
7
|
+
public device: Device,
|
|
8
|
+
public scope: District | Layout
|
|
9
|
+
) { }
|
|
10
|
+
|
|
11
|
+
dump() {
|
|
12
|
+
console.group('Monitor');
|
|
13
|
+
console.log('scope:', this.scope instanceof Layout ? '*' : this.scope.domainName);
|
|
14
|
+
|
|
15
|
+
this.device.dump();
|
|
16
|
+
|
|
17
|
+
console.groupEnd();
|
|
18
|
+
}
|
|
19
|
+
}
|
package/source/position.ts
CHANGED
|
@@ -1,35 +1,55 @@
|
|
|
1
1
|
import { Section } from './section.js';
|
|
2
2
|
|
|
3
3
|
export class SectionPosition {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
4
|
+
constructor(
|
|
5
|
+
public section: Section,
|
|
6
|
+
public offset: number,
|
|
7
|
+
public reversed: boolean
|
|
8
|
+
) {
|
|
9
|
+
if (offset > section.length || offset < 0) {
|
|
10
|
+
throw new Error(`Offset ${offset} out of range for section '${section.domainName}' (0 - ${section.length})`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// returns the absolute position of the point inside the section
|
|
15
|
+
// regardless of direction
|
|
16
|
+
get absolutePosition() {
|
|
17
|
+
if (this.reversed) {
|
|
18
|
+
return this.section.length - this.offset;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return this.offset;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// TODO verify reverse
|
|
25
|
+
advance(distance: number): SectionPosition {
|
|
26
|
+
if (distance < 0) {
|
|
27
|
+
return this.invert().advance(-distance).invert();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this.offset + distance > this.section.length) {
|
|
31
|
+
const next = this.section.next(this.reversed);
|
|
32
|
+
|
|
33
|
+
if (!next) {
|
|
34
|
+
throw new Error(`Illegal advancement ${this} + ${distance}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return new SectionPosition(next, 0, this.reversed).advance(this.offset + distance - this.section.length);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return new SectionPosition(this.section, this.offset + distance, this.reversed);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// reverse direction
|
|
44
|
+
private invert() {
|
|
45
|
+
return new SectionPosition(this.section, this.section.length - this.offset, !this.reversed);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
toString() {
|
|
49
|
+
return `${this.section.name} @ ${this.offset.toFixed(1)} ${this.reversed ? 'backward' : 'forward'}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
toPackTrackValue() {
|
|
53
|
+
return `${this.section.domainName}@${this.offset}${this.reversed ? 'R' : 'F'}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
package/source/span.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { SectionPosition } from "./position";
|
|
2
|
+
import { Section } from "./section";
|
|
3
|
+
|
|
4
|
+
export class Span {
|
|
5
|
+
constructor(
|
|
6
|
+
public head: SectionPosition,
|
|
7
|
+
public inside: Section[],
|
|
8
|
+
public tail: SectionPosition
|
|
9
|
+
) { }
|
|
10
|
+
|
|
11
|
+
// TODO verify reverse
|
|
12
|
+
contains(position: SectionPosition) {
|
|
13
|
+
if (this.inside.includes(position.section)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (position.section == this.head.section) {
|
|
18
|
+
if (this.head.reversed) {
|
|
19
|
+
if (this.head.absolutePosition > position.absolutePosition) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
if (this.head.offset < position.absolutePosition) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (position.section == this.tail.section) {
|
|
30
|
+
if (this.tail.reversed) {
|
|
31
|
+
if (this.tail.absolutePosition < position.absolutePosition) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
if (this.tail.offset > position.absolutePosition) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
overlap(peer: Span) {
|
|
45
|
+
return this.contains(peer.head) || this.contains(peer.tail);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get length() {
|
|
49
|
+
let length = 0;
|
|
50
|
+
|
|
51
|
+
if (this.head.reversed) {
|
|
52
|
+
length += this.head.section.length - this.head.offset;
|
|
53
|
+
} else {
|
|
54
|
+
length += this.head.offset;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (let section of this.inside) {
|
|
58
|
+
length += section.length;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this.tail.reversed) {
|
|
62
|
+
length += this.tail.section.length - this.tail.offset;
|
|
63
|
+
} else {
|
|
64
|
+
length += this.tail.offset;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return length;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// TODO verify reverse
|
|
71
|
+
// TODO add efficient algo
|
|
72
|
+
static trail(start: SectionPosition, end: SectionPosition) {
|
|
73
|
+
let head = start;
|
|
74
|
+
|
|
75
|
+
const sections = [];
|
|
76
|
+
const increment = 1;
|
|
77
|
+
|
|
78
|
+
while (head.section != end.section) {
|
|
79
|
+
head = head.advance(increment);
|
|
80
|
+
|
|
81
|
+
if (head.section != start.section && head.section != end.section && !sections.includes(head.section)) {
|
|
82
|
+
sections.push(head.section);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return new Span(start, sections, end);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Device } from "./device";
|
|
2
|
+
import { District } from "./district";
|
|
3
|
+
import { Layout } from "./layout";
|
|
4
|
+
|
|
5
|
+
export class Throttle {
|
|
6
|
+
constructor(
|
|
7
|
+
public device: Device,
|
|
8
|
+
public scope: District | Layout
|
|
9
|
+
) { }
|
|
10
|
+
|
|
11
|
+
dump() {
|
|
12
|
+
console.group('Throttle');
|
|
13
|
+
console.log('scope:', this.scope instanceof Layout ? '*' : this.scope.domainName);
|
|
14
|
+
|
|
15
|
+
this.device.dump();
|
|
16
|
+
|
|
17
|
+
console.groupEnd();
|
|
18
|
+
}
|
|
19
|
+
}
|