UNPKG

@itwin/core-frontend

Version:
196 lines • 9.08 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module Tiles */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TileRequest = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const IModelApp_1 = require("../IModelApp"); const internal_1 = require("./internal"); /** Represents a pending or active request to load the contents of a [[Tile]]. The request coordinates with the [[Tile.requestContent]] to obtain the raw content and * [[Tile.readContent]] to convert the result into a [[RenderGraphic]]. TileRequests are created internally as needed; it is never necessary or useful for external code to create them. * @public * @extensions */ class TileRequest { /** The requested tile. While the request is pending or active, `tile.request` points back to this TileRequest. */ tile; /** The channel via which the request will be executed. */ channel; /** The set of [[TileUser]]s that are awaiting the result of this request. When this becomes empty, the request is canceled because no user cares about it. * @internal */ users; _state; /** Determines the order in which pending requests are pulled off the queue to become active. A tile with a lower priority value takes precedence over one with a higher value. */ priority = 0; /** Constructor */ constructor(tile, user) { this._state = TileRequest.State.Queued; this.tile = tile; this.channel = tile.channel; this.users = IModelApp_1.IModelApp.tileAdmin.getTileUserSetForRequest(user); } /** The set of [[Viewport]]s that are awaiting the result of this request. When this becomes empty, the request is canceled because no user cares about it. */ get viewports() { return internal_1.TileUser.viewportsFromUsers(this.users); } /** The request's current state. */ get state() { return this._state; } /** True if the request has been enqueued but not yet dispatched. */ get isQueued() { return TileRequest.State.Queued === this._state; } /** True if the request has been canceled. */ get isCanceled() { // If iModel was closed, cancel immediately if (this.tile.iModel.tiles.isDisposed) return true; // After we've received the raw tile data, always finish processing it - otherwise tile may end up in limbo (and producing tile content should be faster than re-requesting raw data). if (TileRequest.State.Loading === this._state) return false; // If no user cares about this tile any more, we're canceled. return this.users.isEmpty; } /** The tile tree to which the requested [[Tile]] belongs. */ get tree() { return this.tile.tree; } /** Indicate that the specified user is awaiting the result of this request. * @internal */ addUser(user) { this.users = IModelApp_1.IModelApp.tileAdmin.getTileUserSetForRequest(user, this.users); } /** Transition the request from "queued" to "active", kicking off a series of asynchronous operations usually beginning with an http request, and - * if the request is not subsequently canceled - resulting in either a successfully-loaded Tile, or a failed ("not found") Tile. * @internal */ async dispatch(onHttpResponse) { if (this.isCanceled) return; (0, core_bentley_1.assert)(this._state === TileRequest.State.Queued); this._state = TileRequest.State.Dispatched; let response; let gotResponse = false; try { response = await this.channel.requestContent(this.tile, () => this.isCanceled); gotResponse = true; // Set this now, so our `isCanceled` check can see it. this._state = TileRequest.State.Loading; } catch (err) { if (err.errorNumber && err.errorNumber === core_bentley_1.IModelStatus.ServerTimeout) { // Invalidate scene - if tile is re-selected, it will be re-requested. this.notifyAndClear(); this._state = TileRequest.State.Failed; this.channel.recordTimeout(); } else { // Unknown error - not retryable this.setFailed(); } } // Notify caller that we have finished http activity. onHttpResponse(); if (!gotResponse || this.isCanceled) return; if (undefined === response && this.channel.onNoContent(this)) { // Invalidate scene - if tile is re-selected, it will be re-requested - presumably via a different channel. this.notifyAndClear(); this._state = TileRequest.State.Failed; return; } return this.handleResponse(response); } /** Cancels this request. This leaves the associated Tile's state untouched. * @internal */ cancel() { this.notifyAndClear(); if (TileRequest.State.Dispatched === this._state) this.channel.onActiveRequestCanceled(this); this._state = TileRequest.State.Failed; } /** Invalidates the scene of each [[TileUser]] interested in this request - typically because the request succeeded, failed, or was canceled. */ notify() { this.users.forEach((user) => { if (user.onRequestStateChanged) user.onRequestStateChanged(this); }); } /** Invalidates the scene of each [[TileUser]] interested in this request and clears the set of interested users. */ notifyAndClear() { this.notify(); this.users = IModelApp_1.IModelApp.tileAdmin.emptyTileUserSet; this.tile.request = undefined; } setFailed() { this.notifyAndClear(); this._state = TileRequest.State.Failed; this.tile.setNotFound(); this.channel.recordFailure(); } /** Invoked when the raw tile content becomes available, to convert it into a tile graphic. */ async handleResponse(response) { let content; let data; if (undefined !== response) { if (typeof response === "string") data = (0, core_bentley_1.base64StringToUint8Array)(response); else if (response instanceof Uint8Array || response instanceof core_common_1.ImageSource) data = response; else if (response instanceof ArrayBuffer) data = new Uint8Array(response); else if (typeof response === "object") { if ("content" in response) content = response.content; else if ("data" in response) data = response; } } if (!content && !data) { this.setFailed(); return; } try { const start = Date.now(); if (!content) { (0, core_bentley_1.assert)(undefined !== data); content = await this.tile.readContent(data, IModelApp_1.IModelApp.renderSystem, () => this.isCanceled); if (this.isCanceled) return; } this._state = TileRequest.State.Completed; this.tile.setContent(content); this.notifyAndClear(); this.channel.recordCompletion(this.tile, content, Date.now() - start); } catch { this.setFailed(); } } } exports.TileRequest = TileRequest; /** @public */ (function (TileRequest) { /** The states through which a [[TileRequest]] proceeds. During the first 3 states, the [[Tile]]'s `request` member is defined, * and its [[Tile.LoadStatus]] is computed based on the state of its request. *@ public */ let State; (function (State) { /** Initial state. Request is pending but not yet dispatched. */ State[State["Queued"] = 0] = "Queued"; /** Follows `Queued` when request begins to be actively processed. */ State[State["Dispatched"] = 1] = "Dispatched"; /** Follows `Dispatched` when the response to the request is being converted into tile graphics. */ State[State["Loading"] = 2] = "Loading"; /** Follows `Loading` when tile graphic has successfully been produced. */ State[State["Completed"] = 3] = "Completed"; /** Follows any state in which an error prevents progression, or during which the request was canceled. */ State[State["Failed"] = 4] = "Failed"; })(State = TileRequest.State || (TileRequest.State = {})); })(TileRequest || (exports.TileRequest = TileRequest = {})); //# sourceMappingURL=TileRequest.js.map