@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.
@@ -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;
@@ -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';
@@ -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 district = railway.firstChild;
40
- while (district) {
41
- if (district.tagName == 'district') {
42
- layout.districts.push(layout.loadDistrict(district, layout));
43
+ let child = railway.firstChild;
44
+ while (child) {
45
+ if (child.tagName == 'district') {
46
+ layout.districts.push(layout.loadDistrict(child, layout));
43
47
  }
44
- district = district.nextSibling;
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
- district = railway.firstChild;
56
+ child = railway.firstChild;
47
57
  let index = 0;
48
- while (district) {
49
- if (district.tagName == 'district') {
50
- layout.linkDistrict(district, layout.districts[index]);
58
+ while (child) {
59
+ if (child.tagName == 'district') {
60
+ layout.linkDistrict(child, layout.districts[index]);
51
61
  index++;
52
62
  }
53
- district = district.nextSibling;
63
+ child = child.nextSibling;
54
64
  }
55
65
  }
56
66
  else {
57
- throw new Error(`unsupported railway definition file version '${version}'`);
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
+ }
@@ -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): any;
8
+ advance(distance: number): SectionPosition;
9
+ private invert;
9
10
  toString(): string;
11
+ toPackTrackValue(): string;
10
12
  }
@@ -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(): any;
11
+ get position(): import("..").SectionPosition;
12
12
  dump(): void;
13
13
  }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@packtrack/layout",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "main": ".built/index.js",
5
5
  "typings": ".built/index.d.ts",
6
6
  "sideEffects": false,
@@ -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 district = railway.firstChild;
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
- while (district) {
54
- if (district.tagName == 'district') {
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
- district = district.nextSibling;
71
+ child = child.nextSibling;
59
72
  }
60
73
 
61
- district = railway.firstChild;
74
+ child = railway.firstChild;
62
75
  let index = 0;
63
76
 
64
- while (district) {
65
- if (district.tagName == 'district') {
66
- layout.linkDistrict(district, layout.districts[index]);
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
- district = district.nextSibling;
84
+ child = child.nextSibling;
72
85
  }
73
86
  } else {
74
- throw new Error(`unsupported railway definition file version '${version}'`);
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
+ }
@@ -1,35 +1,55 @@
1
1
  import { Section } from './section.js';
2
2
 
3
3
  export class SectionPosition {
4
- constructor(
5
- public section: Section,
6
- public offset: number,
7
- public reversed: boolean
8
- ) {}
9
-
10
- get absolutePosition() {
11
- if (this.reversed) {
12
- return this.section.length - this.offset;
13
- }
14
-
15
- return this.offset;
16
- }
17
-
18
- advance(distance: number) {
19
- if (this.offset + distance > this.section.length) {
20
- const next = this.section.next(this.reversed);
21
-
22
- if (!next) {
23
- throw new Error(`Illegal advancement ${this} + ${distance}`);
24
- }
25
-
26
- return new SectionPosition(next, 0, this.reversed).advance(this.offset + distance - this.section.length);
27
- }
28
-
29
- return new SectionPosition(this.section, this.offset + distance, this.reversed);
30
- }
31
-
32
- toString() {
33
- return `${this.section.name} @ ${this.offset.toFixed(1)} ${this.reversed ? 'backward' : 'forward'}`;
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
+ }