Committed new Relic Starting point.

Committed new Relic Starting point.
This commit is contained in:
Michael Mainguy 2023-08-17 09:15:51 -05:00
parent fab12ff6ea
commit ceaf6c4fb5
13 changed files with 393 additions and 74 deletions

23
package-lock.json generated
View File

@ -23,6 +23,7 @@
"earcut": "^2.2.4",
"loglevel": "^1.8.1",
"mxgraph": "^4.2.2",
"niceware": "^4.0.0",
"p2p-data-channel": "^1.10.7",
"query-string": "^8.1.0",
"recordrtc": "^5.6.2",
@ -1248,6 +1249,11 @@
"node": ">=8"
}
},
"node_modules/binary-search": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
"integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -2890,6 +2896,15 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/niceware": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/niceware/-/niceware-4.0.0.tgz",
"integrity": "sha512-7uFcxHr5zwNZO4NEMwxK0N+GbqVMGp2YLZ8x/0dx/nxiT0qCt1zL3hZfqawQoq4XiP2OWa665Xid0g/LyoMfQA==",
"dependencies": {
"binary-search": "^1.3.6",
"randombytes": "^2.0.6"
}
},
"node_modules/node-fetch": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
@ -3268,6 +3283,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",

View File

@ -27,6 +27,7 @@
"earcut": "^2.2.4",
"loglevel": "^1.8.1",
"mxgraph": "^4.2.2",
"niceware": "^4.0.0",
"p2p-data-channel": "^1.10.7",
"query-string": "^8.1.0",
"recordrtc": "^5.6.2",

View File

@ -96,12 +96,15 @@ export class App {
const diagramManager = new DiagramManager(scene, xr.baseExperience, controllers);
const rig = new Rigplatform(scene, xr, diagramManager, controllers);
const toolbox = new Toolbox(scene, xr.baseExperience, diagramManager, controllers);
//const dioManager = new DrawioManager(scene, diagramManager);
import ('./integration/indexdbPersistenceManager').then((module) => {
const persistenceManager = new module.IndexdbPersistenceManager("diagram");
diagramManager.setPersistenceManager(persistenceManager);
AppConfig.config.setPersistenceManager(persistenceManager);
persistenceManager.initialize();
//const newRelicData = new NewRelicData(persistenceManager, scene);
});
});

View File

@ -3,7 +3,6 @@ import {Scene, Vector3, WebXRControllerComponent, WebXRDefaultExperience, WebXRI
import {Controllers} from "./controllers";
import log from "loglevel";
import {DiagramManager} from "../diagram/diagramManager";
import {NewRelicData} from "../integration/newRelic/newRelicData";
export class Right extends Base {
constructor(controller:
@ -22,8 +21,6 @@ export class Right extends Base {
if (bbutton) {
bbutton.onButtonStateChangedObservable.add((button) => {
if (button.pressed) {
const newRelic = new NewRelicData();
newRelic.getNewRelicData();
this.controllers.controllerObserver.notifyObservers({type: 'b-button', value: button.value});
}
});

View File

@ -27,6 +27,10 @@ export interface IPersistenceManager {
addDiagram(diagram: DiagramListing);
getNewRelicData(): Promise<any[]>;
setNewRelicData(data: any): Promise<any>;
removeDiagram(diagram: DiagramListing);
add(mesh: AbstractMesh);

View File

@ -16,10 +16,11 @@ export class IndexdbPersistenceManager implements IPersistenceManager {
constructor(name: string) {
this.db = new Dexie(name);
const version = 3;
const version = 5;
this.db.version(version).stores({config: "id,gridSnap,rotateSnap,createSnap"});
this.db.version(version).stores({entities: "id,diagramlistingid,position,rotation,last_seen,template,text,scale,color"});
this.db.version(version).stores({diagramlisting: "id,name,description,sharekey"});
this.db.version(version).stores({newRelicData: "id,priority, incidentId"});
this.logger.debug("IndexdbPersistenceManager constructed");
}
@ -83,6 +84,17 @@ export class IndexdbPersistenceManager implements IPersistenceManager {
this.diagramListingObserver.notifyObservers(event);
}
public async setNewRelicData(data: any[]) {
this.db["newRelicData"].clear();
data.forEach((d) => {
this.db["newRelicData"].add(d);
});
}
public async getNewRelicData(): Promise<any[]> {
return this.db["newRelicData"].toArray();
}
public async initialize() {
this.logger.info('initialize', this.db['entities'].length);
const configs = await this.db['config'].toArray();

View File

@ -1,18 +1,199 @@
import {IPersistenceManager} from "../iPersistenceManager";
import {
AbstractMesh,
Color3,
DynamicTexture,
InstancedMesh,
MeshBuilder,
Scene,
StandardMaterial,
Vector3
} from "@babylonjs/core";
export class NewRelicData {
private readonly key: string;
private readonly account: string;
private key: string;
private account: string;
private data: any[];
private scene: Scene;
private persistenceManager: IPersistenceManager;
private policyLabels: AbstractMesh[] = [];
constructor() {
constructor(persistenceManager: IPersistenceManager, scene: Scene) {
this.persistenceManager = persistenceManager;
this.scene = scene;
this.persistenceManager.getNewRelicData()
.then((data) => {
this.data = data;
this.getNewRelicData().then(() => {
this.drawGraph();
});
});
}
public setCredentials(key: string, account: string) {
this.key = key;
this.account = account;
}
public async clearData() {
this.data = [];
await this.persistenceManager.setNewRelicData(this.data);
}
public async getNewRelicData() {
try {
if (this.data && this.data.length > 0) {
console.warn("Already have data, early return");
return;
}
try {
const res = await fetch('https://deepdiagram.com/.netlify/functions/nerdgraph', {
method: 'POST',
credentials: 'include',
body: '{"query": "{actor { nrql(query: \\"select * from NrAiIncident \\", accounts: ' + this.account + ') { results } } }"}',
headers: {"Api-Key": this.key}
});
const data = await res.json();
if (data?.data?.actor?.nrql?.results) {
const newdata = data.data.actor.nrql.results.map((item: any) => {
item.id = item.incidentId;
item.policyName = item.policyName ? item.policyName : "No Policy";
return item
});
await this.persistenceManager.setNewRelicData(newdata);
this.data = newdata;
}
console.log(JSON.stringify(data, null, 2));
} catch (err) {
console.log(err);
}
}
public drawGraph() {
this.data.sort((a, b) => {
return parseInt(a.openTime) - parseInt(b.openTime);
});
const duration = this.data[this.data.length - 1].openTime - this.data[0].openTime;
console.log(duration);
const interval = 10 / duration;
const first = parseInt(this.data[0].openTime);
const material = new StandardMaterial("material", this.scene);
material.diffuseColor = new Color3(0, 0, .7);
material.alpha = .3;
const baseMesh = MeshBuilder.CreateBox("baseItem", {width: 1, height: 1, depth: 1}, this.scene);
baseMesh.material = material;
const warningMaterial = new StandardMaterial("warningMaterial", this.scene);
warningMaterial.diffuseColor = new Color3(.7, .7, .2);
warningMaterial.alpha = .5;
const warningMesh = MeshBuilder.CreateBox("warningItem", {width: 1, height: 1, depth: 1}, this.scene);
warningMesh.material = warningMaterial;
const criticalMaterial = new StandardMaterial("criticalMaterial", this.scene);
criticalMaterial.diffuseColor = new Color3(.9, .2, .2);
criticalMaterial.alpha = .7;
const criticalMesh = MeshBuilder.CreateBox("criticalItem", {width: 1, height: 1, depth: 1}, this.scene);
criticalMesh.material = criticalMaterial;
const policies: Map<String, { x: number, y: number }> = new Map<string, { x: number, y: number }>();
this.data.forEach((item) => {
const policy = item.policyName ? item.policyName : "No Policy";
let x = 0;
let y: number = 0;
if (policies.has(policy)) {
const value = policies.get(policy);
x = value.x;
y = value.y + .105;
policies.set(policy, {x, y});
} else {
policies.set(policy, {x: policies.size / 10, y: 0});
x = policies.get(policy).x;
const policyLabel = this.buildText(policy);
policyLabel.scaling = new Vector3(3, 3, 3);
policyLabel.position = new Vector3(x, .4, 0);
policyLabel.rotation.x = Math.PI / 2;
policyLabel.rotation.y = -Math.PI / 2;
this.policyLabels.push(policyLabel);
}
const start = parseInt(item.openTime) - first;
let end = duration;
if (item.closeTime) {
end = parseInt(item.closeTime) - first;
}
let box: AbstractMesh;
switch (item.priority) {
case "critical":
box = new InstancedMesh(item.id, criticalMesh);
break;
case "warning":
box = new InstancedMesh(item.id, warningMesh);
break;
default:
box = new InstancedMesh(item.id, baseMesh);
}
box.position = new Vector3(x, y + .5, (start * interval));
if (item.closeTime) {
box.scaling = new Vector3(.1, .1, (end - start) * interval);
} else {
box.scaling = new Vector3(.1, .1, .01);
}
box.position.z = box.position.z + box.scaling.z / 2;
const startLabel = this.buildText(new Date(start + first).toLocaleString());
startLabel.position = box.position.add(new Vector3(.05, .05, 0));
startLabel.position.z = (start * interval) - .01;
const endLabel = this.buildText(new Date(end + first).toLocaleString());
endLabel.position = box.position.add(new Vector3(.05, .05, 0));
endLabel.position.z = (end * interval) + .01;
});
this.scene.onBeforeRenderObservable.add(() => {
this.policyLabels.forEach((label) => {
label.position.z = this.scene.activeCamera.globalPosition.z;
});
});
}
private buildText(text: string) {
//Set font
const height = 0.03;
const font_size = 24;
const font = "bold " + font_size + "px Arial";
//Set height for dynamic texture
const DTHeight = 1.5 * font_size; //or set as wished
//Calc Ratio
const ratio = height / DTHeight;
//Use a temporary dynamic texture to calculate the length of the text on the dynamic texture canvas
const temp = new DynamicTexture("DynamicTexture", 32, this.scene);
const tmpctx = temp.getContext();
tmpctx.font = font;
const DTWidth = tmpctx.measureText(text).width + 8;
//Calculate width the plane has to be
const planeWidth = DTWidth * ratio;
//Create dynamic texture and write the text
const dynamicTexture = new DynamicTexture("DynamicTexture", {
width: DTWidth,
height: DTHeight
}, this.scene, false);
const mat = new StandardMaterial("mat", this.scene);
mat.diffuseTexture = dynamicTexture;
dynamicTexture.drawText(text, null, null, font, "#000000", "#ffffff", true);
//Create plane and set dynamic texture as material
const plane = MeshBuilder.CreatePlane("text", {width: planeWidth, height: height}, this.scene);
plane.material = mat;
return plane;
}
}

15
src/menus/baseMenu.ts Normal file
View File

@ -0,0 +1,15 @@
import {Scene, WebXRExperienceHelper} from "@babylonjs/core";
import {Controllers} from "../controllers/controllers";
export class BaseMenu {
protected scene: Scene;
protected xr: WebXRExperienceHelper;
protected controllers: Controllers;
constructor(scene: Scene, xr: WebXRExperienceHelper, controllers: Controllers) {
this.scene = scene;
this.xr = xr;
this.controllers = controllers;
}
}

View File

@ -5,19 +5,15 @@ import log from "loglevel";
import {AppConfig} from "../util/appConfig";
import {Controllers} from "../controllers/controllers";
import {DiaSounds} from "../util/diaSounds";
import {BaseMenu} from "./baseMenu";
export class ConfigMenu {
private readonly scene: Scene;
private readonly xr: WebXRExperienceHelper;
export class ConfigMenu extends BaseMenu {
private configPlane: AbstractMesh = null;
private controllers: Controllers;
private yObserver;
constructor(scene: Scene, xr: WebXRExperienceHelper, controllers: Controllers) {
this.scene = scene;
this.xr = xr;
this.controllers = controllers;
super(scene, xr, controllers);
if (!this.yObserver) {
this.controllers.controllerObserver.add((event) => {
if (event.type == 'y-button') {

View File

@ -11,7 +11,7 @@ import {
Vector3,
WebXRDefaultExperience
} from "@babylonjs/core";
import {Button3D, GUI3DManager, StackPanel3D, TextBlock} from "@babylonjs/gui";
import {Button3D, GUI3DManager, PlanePanel, TextBlock} from "@babylonjs/gui";
import {DiagramManager} from "../diagram/diagramManager";
import {EditMenuState} from "./editMenuState";
import {DiagramEvent, DiagramEventType} from "../diagram/diagramEntity";
@ -23,6 +23,7 @@ import {CameraHelper} from "../util/cameraHelper";
import {TextLabel} from "../diagram/textLabel";
import {DiagramConnection} from "../diagram/diagramConnection";
import {GLTF2Export} from "@babylonjs/serializers";
import {AppConfig} from "../util/appConfig";
export class EditMenu {
private state: EditMenuState = EditMenuState.NONE;
@ -34,60 +35,8 @@ export class EditMenu {
private readonly xr: WebXRDefaultExperience;
private readonly diagramManager: DiagramManager;
private connection: DiagramConnection = null;
private panel: StackPanel3D;
constructor(scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager) {
this.scene = scene;
this.xr = xr;
this.diagramManager = diagramManager;
this.gizmoManager = new GizmoManager(scene);
this.gizmoManager.boundingBoxGizmoEnabled = true;
this.gizmoManager.gizmos.boundingBoxGizmo.scaleBoxSize = .020;
this.gizmoManager.gizmos.boundingBoxGizmo.rotationSphereSize = .020;
this.gizmoManager.gizmos.boundingBoxGizmo.scaleDragSpeed = 2;
this.gizmoManager.clearGizmoOnEmptyPointerEvent = true;
this.gizmoManager.usePointerToAttachGizmos = false;
this.manager = new GUI3DManager(this.scene);
const panel = new StackPanel3D();
this.manager.addControl(panel);
panel.addControl(this.makeButton("Modify", "modify"));
panel.addControl(this.makeButton("Remove", "remove"));
panel.addControl(this.makeButton("Add Label", "label"));
panel.addControl(this.makeButton("Copy", "copy"));
panel.addControl(this.makeButton("Connect", "connect"));
panel.addControl(this.makeButton("Export", "export"));
panel.addControl(this.makeButton("Recolor", "recolor"));
//panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras"));
this.manager.controlScaling = .1;
this.scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case PointerEventTypes.POINTERPICK:
const pickedMesh = pointerInfo.pickInfo?.pickedMesh;
if (pickedMesh.metadata?.template &&
pickedMesh?.parent?.parent?.id != "toolbox") {
this.diagramEntityPicked(pointerInfo).then(() => {
this.logger.debug("handled");
}).catch((e) => {
this.logger.error(e);
});
break;
} else {
const tool = pickedMesh?.metadata?.tool;
if (tool) {
this.logger.debug("tool type", tool);
this.paintColor = (pickedMesh.material as StandardMaterial).diffuseColor.toHexString();
this.logger.debug((pickedMesh.material as StandardMaterial).diffuseColor.toHexString());
this.logger.debug(pickedMesh.id);
}
}
}
});
this.panel = panel;
this.isVisible = false;
}
private panel: PlanePanel;
private buttonMaterial: StandardMaterial;
private get isVisible(): boolean {
return this.panel.isVisible;
@ -128,13 +77,70 @@ export class EditMenu {
});
}
constructor(scene: Scene, xr: WebXRDefaultExperience, diagramManager: DiagramManager) {
this.scene = scene;
this.xr = xr;
this.diagramManager = diagramManager;
this.gizmoManager = new GizmoManager(scene);
this.gizmoManager.boundingBoxGizmoEnabled = true;
this.gizmoManager.gizmos.boundingBoxGizmo.scaleBoxSize = .020;
this.gizmoManager.gizmos.boundingBoxGizmo.rotationSphereSize = .020;
this.gizmoManager.gizmos.boundingBoxGizmo.scaleDragSpeed = 2;
this.gizmoManager.clearGizmoOnEmptyPointerEvent = true;
this.gizmoManager.usePointerToAttachGizmos = false;
this.manager = new GUI3DManager(this.scene);
const panel = new PlanePanel();
panel.columns = 4;
this.manager.addControl(panel);
this.buttonMaterial = new StandardMaterial("buttonMaterial", this.scene);
this.buttonMaterial.diffuseColor = Color3.FromHexString("#000000");
panel.addControl(this.makeButton("Modify", "modify"));
panel.addControl(this.makeButton("Remove", "remove"));
panel.addControl(this.makeButton("Add Label", "label"));
panel.addControl(this.makeButton("Copy", "copy"));
panel.addControl(this.makeButton("Connect", "connect"));
panel.addControl(this.makeButton("Export", "export"));
panel.addControl(this.makeButton("Recolor", "recolor"));
panel.addControl(this.makeButton("New Relic", "newrelic"));
//panel.addControl(this.makeButton("Add Ring Cameras", "addRingCameras"));
this.manager.controlScaling = .1;
this.scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case PointerEventTypes.POINTERPICK:
const pickedMesh = pointerInfo.pickInfo?.pickedMesh;
if (pickedMesh.metadata?.template &&
pickedMesh?.parent?.parent?.id != "toolbox") {
this.diagramEntityPicked(pointerInfo).then(() => {
this.logger.debug("handled");
}).catch((e) => {
this.logger.error(e);
});
break;
} else {
const tool = pickedMesh?.metadata?.tool;
if (tool) {
this.logger.debug("tool type", tool);
this.paintColor = (pickedMesh.material as StandardMaterial).diffuseColor.toHexString();
this.logger.debug((pickedMesh.material as StandardMaterial).diffuseColor.toHexString());
this.logger.debug(pickedMesh.id);
}
}
}
});
this.panel = panel;
this.isVisible = false;
}
makeButton(name: string, id: string) {
const button = new Button3D(name);
button.scaling = new Vector3(.1, .1, .1);
button.name = id;
const text = new TextBlock(name, name);
text.fontSize = "24px";
text.color = "white";
text.fontSize = "48px";
text.color = "#ffffff";
text.alpha = 1;
button.content = text;
button.onPointerClickObservable.add(this.handleClick, -1, false, this);
return button;
@ -250,6 +256,22 @@ export class EditMenu {
}
private showNewRelic() {
const inputTextView = new InputTextView({xr: this.xr, scene: this.scene, text: "New Relic"});
inputTextView.show();
inputTextView.onTextObservable.addOnce((value) => {
console.log(value.text);
AppConfig.config.newRelicKey = value.text;
inputTextView.show();
inputTextView.onTextObservable.addOnce((value) => {
console.log(value.text);
AppConfig.config.newRelicAccount = value.text;
});
});
}
private handleClick(_info, state) {
switch (state.currentTarget.name) {
case "modify":
@ -270,6 +292,9 @@ export class EditMenu {
case "recolor":
this.state = EditMenuState.RECOLORING;
break;
case "newrelic":
this.showNewRelic();
break;
case "export":
GLTF2Export.GLTFAsync(this.scene, 'diagram.gltf', {
shouldExportNode: function (node) {

View File

@ -0,0 +1,35 @@
import {AbstractMesh, MeshBuilder, Scene, WebXRExperienceHelper} from "@babylonjs/core";
import {Controllers} from "../controllers/controllers";
import {BaseMenu} from "./baseMenu";
import {AdvancedDynamicTexture, Grid, TextBlock} from "@babylonjs/gui";
import {CameraHelper} from "../util/cameraHelper";
export class IntegrationMenu extends BaseMenu {
private plane: AbstractMesh = null;
constructor(scene: Scene, xr: WebXRExperienceHelper, controllers: Controllers) {
super(scene, xr, controllers);
this.buildMenu();
}
public toggle() {
this.plane.isVisible = !this.plane.isVisible;
}
private buildMenu() {
this.plane = MeshBuilder.CreatePlane("plane", {size: 1}, this.scene);
const advancedTexture2 = AdvancedDynamicTexture.CreateForMesh(this.plane, 1024, 1024, false);
const grid = new Grid("grid");
advancedTexture2.addControl(grid);
grid.addColumnDefinition(.25);
grid.addColumnDefinition(.75);
const labelText1 = new TextBlock("labelText1", "New Relic Key");
grid.addControl(labelText1, 0, 0);
const labelText2 = new TextBlock("labelText1", "New Relic Account");
grid.addControl(labelText2, 1, 0);
CameraHelper.setMenuPosition(this.plane, this.scene);
}
}

View File

@ -16,6 +16,12 @@ export class AppConfig {
private _turnSnap = 0;
private rotateSnap = 0;
private createSnap = 0;
private _newRelicKey: string = null;
public get newRelicKey(): string {
return this._newRelicKey;
}
_physicsEnabled = false;
private readonly defaultGridSnapIndex = 1;
private persistenceManager: IPersistenceManager = null;
@ -46,6 +52,22 @@ export class AppConfig {
return this.gridSnapArray[this.gridSnap];
}
public set newRelicKey(val: string) {
this._newRelicKey = val;
this.save();
}
private _newRelicAccount: string = null;
public get newRelicAccount(): string {
return this._newRelicAccount;
}
public set newRelicAccount(val: string) {
this._newRelicAccount = val;
this.save();
}
public get physicsEnabled(): boolean {
return this._physicsEnabled;
}
@ -173,7 +195,9 @@ export class AppConfig {
rotateSnap: this.currentRotateSnap.value,
createSnap: this.currentCreateSnap.value,
turnSnap: this.currentTurnSnap.value,
physicsEnabled: this._physicsEnabled
physicsEnabled: this._physicsEnabled,
newRelicKey: this._newRelicKey,
newRelicAccount: this._newRelicAccount
});
}

View File

@ -4,5 +4,8 @@ export type AppConfigType = {
rotateSnap: number,
createSnap: number,
turnSnap: number,
physicsEnabled: boolean
physicsEnabled: boolean,
newRelicKey: string,
newRelicAccount: string,
}