abort plotting on dashboards

This commit is contained in:
2023-03-19 09:16:50 +01:00
parent 6d2e8da805
commit a3aa62aee2
6 changed files with 100 additions and 23 deletions

View File

@@ -1,5 +1,7 @@
package org.lucares.pdb.api;
import java.util.concurrent.TimeUnit;
public class AbortException extends RuntimeException {
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();
}
}
}

View File

@@ -40,7 +40,8 @@ export class DashboardComponent implements OnInit {
this.repairArrangement();
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);
@@ -64,17 +65,25 @@ export class DashboardComponent implements OnInit {
loadImages(index: number, plotWidgetQueue: PlotWidgetRenderData[]) {
if (index < plotWidgetQueue.length){
const plot = plotWidgetQueue[index];
const request = PlotWidget.createPlotRequest(plot.widget);
this.plotService.sendPlotRequest(request).subscribe({
next: (response: PlotResponse)=> {
plot.plotResponse= response;
},
error: (error:any)=> {},
complete: () => {
this.loadImages(index +1 , plotWidgetQueue);
}
});
if (plot.isAborted) {
this.loadImages(index +1 , plotWidgetQueue);
}else{
const request = PlotWidget.createPlotRequest(plot.widget, plot.submitterId);
this.plotService.sendPlotRequest(request).subscribe({
next: (response: PlotResponse)=> {
plot.plotResponse= response;
},
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);
this.dashboard!.plots.push(widget);
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);
}
});

View File

@@ -16,7 +16,7 @@
width: 402px;
height: 302px;
}
img {
img.render-img {
cursor: zoom-in;
}
@@ -38,16 +38,51 @@
.dashboard-card:hover .editable-hovered {
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>
<div class="dashboard-card" [ngClass]="{'size-medium' : true}">
<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)="delete()"><img src="/assets/img/recycle-bin-line.svg"/></button>
</div>
<mat-spinner *ngIf="!hasRender('main') && !isError"></mat-spinner>
<img *ngIf="hasRender('main')" [src]="getImageUrl('main')" (click)="showFullScreenImage()" />
<div *ngIf="isError">
<div *ngIf="!hasRender('main') && !data?.error && !data.isAborted">
<button mat-button (click)="abort()"><span class="invader spinner"></span>Cancel</button>
</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!
</div>
<div *ngIf="data?.isAborted" class="aborted-img">
<img src="assets/img/image-aborted.svg" />
</div>
</div>

View File

@@ -17,7 +17,6 @@ export class PlotWidgetComponent {
public thumbnailUrl = "";
isError = false;
@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() {
this.dialog
.open(ConfirmationDialogComponent, {
@@ -67,16 +77,16 @@ export class PlotWidgetComponent {
if (config !== undefined && config.query.length > 0) {
this.data.widget.config = config;
this.isError = false;
this.data.error = false;
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({
next: (response: PlotResponse)=> {
this.data.plotResponse = response;
},
error: (error:any)=> {
this.isError = true;
this.data.error = true;
},
});
}

View File

@@ -68,7 +68,7 @@ export class PlotWidget extends BaseWidget {
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 width = this.width(widget.size);
@@ -77,7 +77,7 @@ export class PlotWidget extends BaseWidget {
const fullHeight = window.innerHeight-30;
const request = new PlotRequest(
(<any>window).submitterId+(<any>window).randomId(),
submitterId,
widget.config,
{
'main': new RenderOptions(height,width, false, true),
@@ -115,7 +115,9 @@ export type PlotSize = 'SMALL'|'MEDIUM'|'LARGE';
export type PlotType = 'TEXT'|'PLOT';
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) {
}
}

View 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