abort plotting on dashboards
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
package org.lucares.pdb.api;
|
package org.lucares.pdb.api;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class AbortException extends RuntimeException {
|
public class AbortException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 7614132985675048490L;
|
private static final long serialVersionUID = 7614132985675048490L;
|
||||||
@@ -29,4 +31,18 @@ public class AbortException extends RuntimeException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sleepAbortibly(final long millis) throws AbortException {
|
||||||
|
final long deadline = System.currentTimeMillis() + millis;
|
||||||
|
|
||||||
|
while (System.currentTimeMillis() < deadline) {
|
||||||
|
try {
|
||||||
|
TimeUnit.MILLISECONDS.sleep(Math.min(10, deadline - System.currentTimeMillis()));
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new AbortException();
|
||||||
|
}
|
||||||
|
AbortException.abortIfInterrupted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ export class DashboardComponent implements OnInit {
|
|||||||
this.repairArrangement();
|
this.repairArrangement();
|
||||||
|
|
||||||
dashboard.plots.forEach(p => {
|
dashboard.plots.forEach(p => {
|
||||||
this.plotWidgetRenderData.push(new PlotWidgetRenderData(p));
|
const submitterId = (<any>window).submitterId + (<any>window).randomId();
|
||||||
|
this.plotWidgetRenderData.push(new PlotWidgetRenderData(p, submitterId));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.loadImages(0, this.plotWidgetRenderData);
|
this.loadImages(0, this.plotWidgetRenderData);
|
||||||
@@ -64,17 +65,25 @@ export class DashboardComponent implements OnInit {
|
|||||||
loadImages(index: number, plotWidgetQueue: PlotWidgetRenderData[]) {
|
loadImages(index: number, plotWidgetQueue: PlotWidgetRenderData[]) {
|
||||||
|
|
||||||
if (index < plotWidgetQueue.length){
|
if (index < plotWidgetQueue.length){
|
||||||
|
|
||||||
const plot = plotWidgetQueue[index];
|
const plot = plotWidgetQueue[index];
|
||||||
const request = PlotWidget.createPlotRequest(plot.widget);
|
if (plot.isAborted) {
|
||||||
this.plotService.sendPlotRequest(request).subscribe({
|
this.loadImages(index +1 , plotWidgetQueue);
|
||||||
next: (response: PlotResponse)=> {
|
}else{
|
||||||
plot.plotResponse= response;
|
const request = PlotWidget.createPlotRequest(plot.widget, plot.submitterId);
|
||||||
},
|
this.plotService.sendPlotRequest(request).subscribe({
|
||||||
error: (error:any)=> {},
|
next: (response: PlotResponse)=> {
|
||||||
complete: () => {
|
plot.plotResponse= response;
|
||||||
this.loadImages(index +1 , plotWidgetQueue);
|
},
|
||||||
}
|
error: (error:any)=> {
|
||||||
});
|
plot.error = error;
|
||||||
|
this.loadImages(index +1 , plotWidgetQueue);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.loadImages(index +1 , plotWidgetQueue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +136,7 @@ export class DashboardComponent implements OnInit {
|
|||||||
const widget = new PlotWidget((<any>window).randomId(), 'MEDIUM', config);
|
const widget = new PlotWidget((<any>window).randomId(), 'MEDIUM', config);
|
||||||
this.dashboard!.plots.push(widget);
|
this.dashboard!.plots.push(widget);
|
||||||
this.dashboard!.arrangement[0].push(widget.id);
|
this.dashboard!.arrangement[0].push(widget.id);
|
||||||
this.plotWidgetRenderData.push(new PlotWidgetRenderData(widget));
|
this.plotWidgetRenderData.push(new PlotWidgetRenderData(widget, (<any>window).randomId()));
|
||||||
this.loadImages(this.plotWidgetRenderData.length-1, this.plotWidgetRenderData);
|
this.loadImages(this.plotWidgetRenderData.length-1, this.plotWidgetRenderData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
width: 402px;
|
width: 402px;
|
||||||
height: 302px;
|
height: 302px;
|
||||||
}
|
}
|
||||||
img {
|
img.render-img {
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,16 +38,51 @@
|
|||||||
.dashboard-card:hover .editable-hovered {
|
.dashboard-card:hover .editable-hovered {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aborted-img {
|
||||||
|
flex-grow: 0.3;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
.aborted-img img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invader {
|
||||||
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAUCAYAAACTQC2+AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kKGRAxBENShygAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAbUlEQVRIx9WVQQrAMAgEM6X///L2FCihYvRg7F416LCsQdKo0DWKZA4CBGzjev1lRHgezS0lkan3IYr485ZFdo5oJZkbWoRWfSWrJ8p6suvZucsgCS8THsHX+zKiO5uLaO763bpobvoS/e6HfQBzIE0PhAsDxgAAAABJRU5ErkJggg==);
|
||||||
|
width: 26px;
|
||||||
|
height: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
.spinner {
|
||||||
|
animation: wobble 2s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes wobble {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="dashboard-card" [ngClass]="{'size-medium' : true}">
|
<div class="dashboard-card" [ngClass]="{'size-medium' : true}">
|
||||||
<div class="editable-hovered top-right">
|
<div class="editable-hovered top-right">
|
||||||
<button mat-icon-button (click)="edit()" ><img src="/assets/img/edit-outline.svg"/></button>
|
<button mat-icon-button (click)="edit()" ><img src="/assets/img/edit-outline.svg"/></button>
|
||||||
<button mat-icon-button (click)="delete()"><img src="/assets/img/recycle-bin-line.svg"/></button>
|
<button mat-icon-button (click)="delete()"><img src="/assets/img/recycle-bin-line.svg"/></button>
|
||||||
</div>
|
</div>
|
||||||
<mat-spinner *ngIf="!hasRender('main') && !isError"></mat-spinner>
|
<div *ngIf="!hasRender('main') && !data?.error && !data.isAborted">
|
||||||
<img *ngIf="hasRender('main')" [src]="getImageUrl('main')" (click)="showFullScreenImage()" />
|
<button mat-button (click)="abort()"><span class="invader spinner"></span>Cancel</button>
|
||||||
<div *ngIf="isError">
|
</div>
|
||||||
|
<img *ngIf="hasRender('main')" [src]="getImageUrl('main')" (click)="showFullScreenImage()" class="render-img" />
|
||||||
|
<div *ngIf="data?.error && !data?.isAborted">
|
||||||
There was an error! This is a good time to panic!
|
There was an error! This is a good time to panic!
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="data?.isAborted" class="aborted-img">
|
||||||
|
<img src="assets/img/image-aborted.svg" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export class PlotWidgetComponent {
|
|||||||
|
|
||||||
public thumbnailUrl = "";
|
public thumbnailUrl = "";
|
||||||
|
|
||||||
isError = false;
|
|
||||||
|
|
||||||
@ViewChild("plotView") plotView!: PlotViewComponent;
|
@ViewChild("plotView") plotView!: PlotViewComponent;
|
||||||
|
|
||||||
@@ -45,6 +44,17 @@ export class PlotWidgetComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abort(){
|
||||||
|
window.console.log("abort");
|
||||||
|
this.data.isAborted = true;
|
||||||
|
this.service.abort(this.data.submitterId).subscribe({
|
||||||
|
complete: () => {
|
||||||
|
window.console.log("cancelled");
|
||||||
|
},
|
||||||
|
error: () => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.dialog
|
this.dialog
|
||||||
.open(ConfirmationDialogComponent, {
|
.open(ConfirmationDialogComponent, {
|
||||||
@@ -67,16 +77,16 @@ export class PlotWidgetComponent {
|
|||||||
if (config !== undefined && config.query.length > 0) {
|
if (config !== undefined && config.query.length > 0) {
|
||||||
this.data.widget.config = config;
|
this.data.widget.config = config;
|
||||||
|
|
||||||
this.isError = false;
|
this.data.error = false;
|
||||||
this.data.plotResponse = undefined;
|
this.data.plotResponse = undefined;
|
||||||
|
|
||||||
const request = PlotWidget.createPlotRequest(this.data.widget);
|
const request = PlotWidget.createPlotRequest(this.data.widget, this.data.submitterId);
|
||||||
this.service.sendPlotRequest(request).subscribe({
|
this.service.sendPlotRequest(request).subscribe({
|
||||||
next: (response: PlotResponse)=> {
|
next: (response: PlotResponse)=> {
|
||||||
this.data.plotResponse = response;
|
this.data.plotResponse = response;
|
||||||
},
|
},
|
||||||
error: (error:any)=> {
|
error: (error:any)=> {
|
||||||
this.isError = true;
|
this.data.error = true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export class PlotWidget extends BaseWidget {
|
|||||||
super(id, 'PLOT', size);
|
super(id, 'PLOT', size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static createPlotRequest(widget: PlotWidget): PlotRequest {
|
public static createPlotRequest(widget: PlotWidget, submitterId: string): PlotRequest {
|
||||||
|
|
||||||
const height = this.height(widget.size);
|
const height = this.height(widget.size);
|
||||||
const width = this.width(widget.size);
|
const width = this.width(widget.size);
|
||||||
@@ -77,7 +77,7 @@ export class PlotWidget extends BaseWidget {
|
|||||||
const fullHeight = window.innerHeight-30;
|
const fullHeight = window.innerHeight-30;
|
||||||
|
|
||||||
const request = new PlotRequest(
|
const request = new PlotRequest(
|
||||||
(<any>window).submitterId+(<any>window).randomId(),
|
submitterId,
|
||||||
widget.config,
|
widget.config,
|
||||||
{
|
{
|
||||||
'main': new RenderOptions(height,width, false, true),
|
'main': new RenderOptions(height,width, false, true),
|
||||||
@@ -115,7 +115,9 @@ export type PlotSize = 'SMALL'|'MEDIUM'|'LARGE';
|
|||||||
export type PlotType = 'TEXT'|'PLOT';
|
export type PlotType = 'TEXT'|'PLOT';
|
||||||
|
|
||||||
export class PlotWidgetRenderData {
|
export class PlotWidgetRenderData {
|
||||||
constructor(public widget: PlotWidget, public plotResponse?: PlotResponse) {
|
public isAborted = false;
|
||||||
|
public error: string|boolean = false;
|
||||||
|
constructor(public widget: PlotWidget, public submitterId: string, public plotResponse?: PlotResponse) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
pdb-js/src/assets/img/image-aborted.svg
Normal file
5
pdb-js/src/assets/img/image-aborted.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="-30 -30 700 700">
|
||||||
|
<path d="M-.012 65.611h640.024v508.778H-.012V65.611zm180.026 132.273c22.996 0 41.635 18.638 41.635 41.634 0 22.997-18.638 41.635-41.635 41.635-22.996 0-41.634-18.638-41.634-41.635 0-22.996 18.638-41.634 41.634-41.634zm175.207 178.679l83.269-143.978 88.466 223.763h-412.86v-27.756l34.702-1.725 34.69-85.005 17.338 60.722h52.052l45.095-116.222 57.248 90.201zM47.528 107.764h544.944v424.47H47.528v-424.47z"/>
|
||||||
|
|
||||||
|
<line x1="10" y1="630" x2="630" y2="10" style="stroke:#000; stroke-width: 60px; stroke-linecap: round;" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 758 B |
Reference in New Issue
Block a user