mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-02-28 17:39:36 +00:00
1. Implement new method to open pop-up windows that schedules pop-up opens through a queue, delaying between opens, and retrying with longer delays when the opens fail. This is to satisfy new restrictions on pop-ups introduced in Apple Safari 11.0, even when the browser is configured to enable pop-ups. 2. Remove the Application Cache facility. This API has been deprecated and will be removed from browsers. 3. Implement improved setCallback() asynchronous timing mechanism with a new Promise-based approach developed for the retro-220 emulator. 4. Correct Processor memory access logic to avoid causing an Invalid Address interrupt in Control State. 5. Correct Processor Exit Character Mode (XX00) by implementing it separately from Word Mode exits. 6. Implement separate jump mechanisms for Word and Character Mode to correct edge-case errors. 7. Make slight correction to clock counting in singlePrecisionAdd. 8. Fix bug in double-clicking the "stacker" of the card punch to extract its contents. 9. Disable I/O descriptor word count in IOUnit for card reader/punch operations; remove extraneous blank card between card-load programs from the COLDSTART-XIII.card and COOLSTART-XIII.card decks that was previously required to prevent "eating" the second program's boot card. 10. Remove extraneous whitespace from B5500FramePaper used for most text-only windows and frames. 11. Compute HPT disk latency based on current timestamp rather than a random number. 12. Correct top-of-form handling in B5500LinePrinter. 13. Correct annimation of tape reel in B5500MagTapeDrive. 14. Replace the emulator's custom bindMethod() utility routine with Function.bind(). 15. Replace the emulator's custom DOM className utility routines by DOM classList methods. 16. Add P1 S and F register values to the internal tape dump caption; improve the dump tape's label records. 17. Give the annunciators on the ConsolePanel a dark gray presence when they are not lit. 18. Correct the method to "focus" the ConsolePanel window after the SPO initializes and becomes ready. 19. Clean up line delimiters in source and make consistent for Windows systems.
760 lines
32 KiB
JavaScript
760 lines
32 KiB
JavaScript
/***********************************************************************
|
|
* retro-b5500/webUI B5500DiskStorageConfig.js
|
|
************************************************************************
|
|
* Copyright (c) 2014, Nigel Williams and Paul Kimpel.
|
|
* Licensed under the MIT License, see
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
************************************************************************
|
|
* B5500 Disk Storage Configuration management object.
|
|
*
|
|
* Defines the disk configuration used internally by the emulator, along
|
|
* with the methods used to manage that configuration data.
|
|
*
|
|
* The configuration data consists of two parts, (a) the processors, I/O
|
|
* units, memory modules, and peripheral devices that make up the system,
|
|
* and (b) a disk storage configuration that defines the disk Electronics
|
|
* Units (EU) and sizes and types of each of the EUs. This module addresses
|
|
* the second part.
|
|
*
|
|
* Each disk storage subsystem is maintained as a separate IndexedDB data base.
|
|
* Each data base contains a single-object store named "CONFIG" that holds the
|
|
* configuration data for that subsystem. In addition, there are one or more
|
|
* "EUn" object stores, where "n" is a one- or two-digit decimal number in the
|
|
* range 0-19. Each of these object stores represents the storage capacity for
|
|
* one Electronics Unit and its associated Storage Units (SU). At present, each
|
|
* object in an EU store represents one segment of 30 B5500 words (240 6-bit
|
|
* characters) and is implemented as a Uint8Array(240). The SUs are not
|
|
* represented individually, as their address space was monolithic within an EU
|
|
* (i.e., one I/O could cross SU boundaries but not EU boundaries).
|
|
*
|
|
* In addition to the "CONFIG" and "EUn" object stores, the data base will have
|
|
* two object stores reserved for use by the drum units, DRA and DRB. These
|
|
* units have a fixed 32K words of storage and were word-addressible. Since
|
|
* they have a fixed configuration, they are not represented in the "CONFIG"
|
|
* object store.
|
|
*
|
|
* The "CONFIG" object store contains one object which in turn contains a set
|
|
* of objects of the form:
|
|
*
|
|
* EUn: {size: 200000, slow: false, lockoutMask: 0}
|
|
*
|
|
* where
|
|
*
|
|
* n: is the EU number (0-19).
|
|
* size: is the capacity of the EU in segments:
|
|
* 40000-200000 for Model-I disk
|
|
* 80000-400000 for Model-IB (slow) disk.
|
|
* slow: indicates Model-I (false) or Model-IB (true) disk,
|
|
* lockoutMask: is a binary integer, the low-order 20 bits of which
|
|
* represent the 20 disk lockout switches. A bit in this
|
|
* mask will be 1 if the associated switch is on. If
|
|
* this integer is negative, that indicates the master
|
|
* lockout switch is on. [not presently implemented]
|
|
*
|
|
* Model-I SU disks rotate at 1500 RPM, thus have an average access time of
|
|
* 20ms. Each Model-I SU stores 40,000 30-word segments. Model-IB (slow) SU
|
|
* disks have twice the capacity but half the speed: they rotate at 750 RPM,
|
|
* and thus have an average access time of 40ms. Each Model-IB SU stores 80,000
|
|
* 30-word segments. The disks have fixed heads, so rotational latency is the
|
|
* only component of access time; there is no delay due to radial head
|
|
* positioning.
|
|
*
|
|
* In addition to the "EUn" structures, the "CONFIG" object contains two global
|
|
* properties:
|
|
*
|
|
* configLevel: the revision level of the CONFIG object.
|
|
* storageName: the name of the storage subsystem. This should match
|
|
* the name of the IndexedDB data base (db.name).
|
|
*
|
|
* The "EUn" objects in "CONFIG" MUST match one-for-one the "EUn" object stores
|
|
* in the data base. Object stores without a matching "CONFIG" entry entry will
|
|
* not be used by the emulator (the EU will be reported as being not ready).
|
|
* "CONFIG" entries without a matching object store will cause an error when the
|
|
* disk device driver attempts to create an IndexedDB transaction that references
|
|
* the missing EU.
|
|
*
|
|
* Once created, EU object stores are permanent and must not be deleted. They
|
|
* also must not be configured with a smaller size than they currently have.
|
|
* These restrictions are designed to prevent problems with the MCP file
|
|
* system, where removing storage space may render some files or parts of
|
|
* files inaccessible. In the configuration UI, you may add EUs, or you may
|
|
* increase the capacity of existing EUs, but you cannot delete or shrink
|
|
* them. Thus, the number of SUs for an existing EU may be increased, and
|
|
* Model-I disk may be changed to Model-IB (slow) disk, but not vice versa.
|
|
*
|
|
* If you need to reduce the storage size of a disk subsystem, your only
|
|
* option is to dump it to tape, delete it, create a new, smaller one, and
|
|
* then load the new subsystem from tape.
|
|
*
|
|
* EU0-9 may be used in any system configuration. EU10-19 may be used only
|
|
* in system configurations that have DKB enabled and do NOT have a Disk File
|
|
* Exchange (DFX) enabled. Any EUs in the range 10-19 will be ignored by a
|
|
* system that has a DFX. For systems without a DFX, EU0-9 will be accessed
|
|
* by DKA; EU11-19 will be accessed by DKB.
|
|
*
|
|
* Note that software support for a DFX is enabled by an MCP compile-time
|
|
* option ("$SET DFX=TRUE"). The setting of the MCP's DFX option *MUST MATCH*
|
|
* the DFX setting in the system configuration.
|
|
*
|
|
* W A R N I N G !
|
|
* ---------------
|
|
* Attempting to run a DFX-enabled MCP on a non-DFX hardware
|
|
* configuration, or vice versa, will likely corrupt the data
|
|
* in the disk subsystem and require a Cold Start to resolve.
|
|
*
|
|
* Also note that in emulator 0.20 and earlier, there was a single disk storage
|
|
* data base named "B5500DiskUnit". That data base also contained a single-object
|
|
* store named "CONFIG", but with a simpler set of "EUn" objects that indicated
|
|
* only the size of the EU. The "B5500DiskUnit" name is retained as a default
|
|
* storage name for the initial disk subsystem to be created. If the emulator
|
|
* encounters a data base with the older form of EU object, it will use it
|
|
* implying slow=true and lockoutMask=0. If you modify the EU configuration in
|
|
* any way, however, the older form of object will be converted to the new form.
|
|
*
|
|
* With this approach, disk subsystem data bases from older versions of the
|
|
* emulator will work with the later versions, but ONLY IF THE DISK CONFIGURATION
|
|
* IS NOT MODIFIED. Once you click the "SAVE" button on the Disk Subsystem
|
|
* Configuration dialog, the "CONFIG" structure will be updated to the newer
|
|
* format, and the data base will no longer work with older versions of the
|
|
* emulator.
|
|
*
|
|
************************************************************************
|
|
* 2014-08-19 P.Kimpel
|
|
* Original version, from B5500SystemConfig.js.
|
|
***********************************************************************/
|
|
"use strict";
|
|
|
|
/**************************************/
|
|
function B5500DiskStorageConfig() {
|
|
/* Constructor for the DiskStorageConfig object */
|
|
|
|
this.db = null; // the IndexedDB data base connection (null if closed)
|
|
this.storageConfig = null; // the current storage subsystem configuration
|
|
this.doc = null; // configuration UI document object
|
|
this.window = null; // configuration UI window object
|
|
this.alertWin = window; // current base window for alert/confirm/prompt
|
|
}
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.dbConfigLevel = 1; // configuration object version
|
|
B5500DiskStorageConfig.prototype.storageConfigName = "CONFIG";
|
|
|
|
// Current disk storage configuration object
|
|
B5500DiskStorageConfig.prototype.storageConfig = {
|
|
configLevel: B5500DiskStorageConfig.prototype.dbConfigLevel,
|
|
storageName: B5500SystemConfiguration.prototype.sysDefaultStorageName
|
|
// The "EUn" strucures will be added here as they are created.
|
|
};
|
|
|
|
// Disk Electronics Unit configuration object
|
|
B5500DiskStorageConfig.prototype.defaultEUConfig = {
|
|
size: 200000, // 5 Model-I SUs
|
|
slow: false, // Model-I SUs
|
|
lockoutMask: 0 // no lockout switches set [not yet implemented]
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.$$ = function $$(id) {
|
|
return this.window.document.getElementById(id);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.genericDBError = function genericDBError(ev) {
|
|
// Formats a generic alert message when an otherwise-unhandled data base error occurs */
|
|
|
|
alert("data base \"" + ev.target.result.name +
|
|
"\" UNHANDLED ERROR: " + ev.target.result.error);
|
|
};
|
|
|
|
|
|
/***********************************************************************
|
|
* Disk Storage data base Configuration Interface *
|
|
***********************************************************************/
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.upgradeStorageSchema = function upgradeStorageSchema(ev) {
|
|
/* Handles the onupgradeneeded event for this Disk Storage data base. Upgrades
|
|
the schema to the current version. For a new data base, creates the default
|
|
configuration and stores it in the data base. "ev" is the upgradeneeded event.
|
|
Must be called in the context of the DiskStorageConfig object */
|
|
var aborted = false;
|
|
var configStore = null;
|
|
var db = ev.target.result;
|
|
var req = ev.target;
|
|
var sysConfig;
|
|
var txn = req.transaction;
|
|
var that = this;
|
|
|
|
function putConfig(config) {
|
|
/* Stores "config" as the configuration object to the "CONFIG"
|
|
object store */
|
|
config.configLevel = B5500DiskStorageConfig.prototype.dbConfigLevel;
|
|
config.storageName = db.name;
|
|
configStore.put(config, 0);
|
|
that.storageConfig = config;
|
|
sysConfig = new B5500SystemConfig();
|
|
sysConfig.addStorageName(db.name, function successor() {});
|
|
}
|
|
|
|
function applyNewConfig(config) {
|
|
/* Applies the new configuration structure in "CONFIG" to the data base.
|
|
If CONFIG contains EU* structures that are not in the data base, those
|
|
new structures are created. If the data base contains EU* structures
|
|
that are not in CONFIG, they are ignored (the storage UI should
|
|
prevent this from happening, however) */
|
|
var name;
|
|
var euRex = /^EU\d{1,2}$/;
|
|
|
|
for (name in config) { // for each property in config
|
|
if (name.search(euRex) == 0) { // filter name for "EUn" or "EUnn"
|
|
if (!db.objectStoreNames.contains(name)) {
|
|
db.createObjectStore(name); // create a new EU store
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the drum units if they do no already exist
|
|
if (!db.objectStoreNames.contains("DRA")) {
|
|
db.createObjectStore("DRA");
|
|
}
|
|
if (!db.objectStoreNames.contains("DRB")) {
|
|
db.createObjectStore("DRB");
|
|
}
|
|
putConfig(config);
|
|
}
|
|
|
|
function createDefaultConfig() {
|
|
/* Create an initial disk subsystem configuration for a new data base,
|
|
containing a single EU having the default EU configuration, plus
|
|
both drum units */
|
|
var config = B5500Util.deepCopy(B5500DiskStorageConfig.prototype.storageConfig);
|
|
var euName = "EU0";
|
|
|
|
db.createObjectStore("DRA"); // unconditionally create both drum units
|
|
db.createObjectStore("DRB");
|
|
db.createObjectStore(euName); // unconditionally create EU0
|
|
config[euName] = B5500Util.deepCopy(B5500DiskStorageConfig.prototype.defaultEUConfig);
|
|
putConfig(config);
|
|
}
|
|
|
|
/*** Start of upgradeStorageSchema ***/
|
|
|
|
txn.onabort = function upgradeOnAbort(ev) {
|
|
// do nothing
|
|
};
|
|
|
|
if (ev.oldVersion < 1) {
|
|
// New data base -- start by creating a store for the config structure.
|
|
if (!this.alertWin.confirm("Disk Storage data base \"" + db.name +
|
|
"\"\ndoes not exist. Do you want to create it?")) {
|
|
aborted = true;
|
|
txn.abort();
|
|
db.close();
|
|
} else {
|
|
this.alertWin.alert("Disk Storage data base \"" + db.name + "\" created.");
|
|
}
|
|
}
|
|
|
|
if (aborted) {
|
|
this.alertWin.alert("Disk Storage data base creation aborted.");
|
|
} else if (db.objectStoreNames.contains(this.storageConfigName)) {
|
|
// Normal upgrade: get the updated config structure from the DB and apply it
|
|
configStore = txn.objectStore(this.storageConfigName);
|
|
configStore.get(0).onsuccess = function(ev) {
|
|
applyNewConfig(that.normalizeStorageConfig(ev.target.result, db.Name));
|
|
};
|
|
} else {
|
|
// No config structure exists in the data base. We assume this is a
|
|
// data base just being created, so simply create a Disk Storage CONFIG
|
|
// structure for it.
|
|
configStore = db.createObjectStore(this.storageConfigName);
|
|
createDefaultConfig();
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.deleteStorageDB = function deleteStorageDB(
|
|
storageName, onsuccess, onfailure) {
|
|
/* Attempts to permanently delete the Disk Storage data base named in
|
|
"storageName". If successful, calls the "onsuccess" function passing
|
|
the resulting event; if not successful, calls onfailure passing the
|
|
resulting event */
|
|
var req;
|
|
var sysConfig;
|
|
var that = this;
|
|
|
|
if (this.alertWin.confirm("This will PERMANENTLY DELETE the\n" +
|
|
"Disk Storage data base \"" + storageName + "\".\n\n" +
|
|
"Are you sure you want to do this?\n")) {
|
|
if (this.alertWin.confirm("Deletion of the Disk Storage data base\n" +
|
|
"CANNOT BE UNDONE.\n\nAre you really sure?\n")) {
|
|
if (this.db && this.db.name == storageName) {
|
|
this.storageConfig = null;
|
|
this.closeStorageDB();
|
|
}
|
|
|
|
req = window.indexedDB.deleteDatabase(storageName);
|
|
|
|
req.onerror = function(ev) {
|
|
that.alertWin.alert("CANNOT DELETE Disk Storage data base:\n" + ev.target.error);
|
|
onfailure(ev);
|
|
};
|
|
|
|
req.onblocked = function(ev) {
|
|
that.alertWin.alert("Deletion of Disk Storage data base is BLOCKED");
|
|
};
|
|
|
|
req.onsuccess = function(ev) {
|
|
that.alertWin.alert("Disk Storage data base \"" + storageName +
|
|
"\"\n successfully deleted.");
|
|
onsuccess(ev);
|
|
sysConfig = new B5500SystemConfig();
|
|
sysConfig.removeStorageName(storageName, function successor() {});
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.modifyStorageSchema =
|
|
function modifyStorageSchema(onsuccess, onfailure) {
|
|
/* Called to trigger a schema upgrade. Before calling this method, the new
|
|
EU configuration must be established and stored in the "CONFIG" store.
|
|
This method simply closes the data base and then reopens with with the next
|
|
higher version number. That will trigger the onupgradeneeded event, and
|
|
upgradeStorageSchema() will examine the new config structure, creating any
|
|
new EUs as necessary. The "onsuccess" or "onfailure" callbacks are called
|
|
as appropriate */
|
|
var dbName = this.db.name;
|
|
var ver = this.db.version;
|
|
var req; // IndexedDB open request
|
|
var that = this;
|
|
|
|
this.closeStorageDB();
|
|
req = window.indexedDB.open(dbName, ver+1); // trigger the schema upgrade
|
|
|
|
req.onblocked = function(ev) {
|
|
that.alertWin.alert("data base \"" + dbName +
|
|
"\"\n open for schema upgrade is blocked");
|
|
};
|
|
|
|
req.onupgradeneeded = function(ev) {
|
|
that.upgradeStorageSchema(ev);
|
|
};
|
|
|
|
req.onerror = function(ev) {
|
|
onfailure(ev);
|
|
that.db = null;
|
|
};
|
|
|
|
req.onsuccess = function(ev) {
|
|
that.db = ev.target.result;
|
|
that.db.onerror = that.genericDBError; // set up global error handler
|
|
// Since we know we just went through an onupgradeneeded event, we know
|
|
// this data base now has a "CONFIG" structure, so the extra tests in
|
|
// openStorageDB() are not necessary.
|
|
// that.alertWin.alert("data base \"" + dbName + "\" schema upgrade successful");
|
|
delete that.storageConfig;
|
|
onsuccess(ev);
|
|
};
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.openStorageDB = function openStorageDB(
|
|
storageName, onsuccess, onfailure) {
|
|
/* Attempts to open the Disk Storage data base named in "storageName".
|
|
Handles, if necessary, a change in data base version. If successful,
|
|
calls the "onsuccess" function passing the success event. If not successful,
|
|
calls the "onfailure" function passing the error event.
|
|
|
|
This process is just a little bit nasty, because the only time we can
|
|
modify the DB schema to add new EUs is during an onupgradeneeded event.
|
|
That event is fired only when the DB version increases. Therefore, we
|
|
must normally open the data base without specifying a version, as we have
|
|
no aprori idea what it might me. There are three possibilities:
|
|
1. This is an open for a data base that does not currently exist.
|
|
The old version is 0 and the new version will be 1. The onupgrade-
|
|
needed event will automatically fire, and upgradeStorageSchema()
|
|
will create the "CONFIG" structure and an initial default EU.
|
|
2. This is an open for an existing data base that has a "CONFIG"
|
|
structure. This is the simplest case, and proceeds like a normal
|
|
IndexedDB open.
|
|
3. This is an open for an existing data base that does not have a
|
|
"CONFIG" structure. We have no idea what it is, so we throw up
|
|
an alert, call onfailure(), and close the data base before we do
|
|
any damage. The failure routine can, if it wishes, still access
|
|
the data base.
|
|
*/
|
|
var req; // IndexedDB open request
|
|
var that = this;
|
|
|
|
req = window.indexedDB.open(storageName); // open the current version, whatever it may be
|
|
|
|
req.onblocked = function(ev) {
|
|
that.alertWin.alert("data base \"" + storageName + "\" open is blocked");
|
|
that.closeStorageDB();
|
|
};
|
|
|
|
req.onupgradeneeded = function(ev) {
|
|
that.upgradeStorageSchema(ev);
|
|
};
|
|
|
|
req.onerror = function(ev) {
|
|
onfailure(ev);
|
|
that.db = null;
|
|
};
|
|
|
|
req.onsuccess = function(ev) {
|
|
that.db = ev.target.result;
|
|
that.db.onerror = that.genericDBError; // set up global error handler
|
|
if (that.db.objectStoreNames.contains(that.storageConfigName)) {
|
|
delete that.storageConfig;
|
|
onsuccess(ev);
|
|
} else {
|
|
that.alertWin.alert("ERROR: Disk Storage data base \"" + storageName +
|
|
"\"\nhas no CONFIG store");
|
|
onfailure(ev);
|
|
that.closeStorageDB();
|
|
}
|
|
};
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.closeStorageDB = function closeStorageDB() {
|
|
/* Closes the IndexedDB instance if it is open */
|
|
|
|
if (this.db) {
|
|
this.db.close();
|
|
this.db = null;
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.normalizeStorageConfig = function normalizeStorageConfig(config, storageName) {
|
|
/* Reformats as necessary an older-format storage CONFIG structure to the
|
|
current structure format and returns the current format */
|
|
var eu;
|
|
var name;
|
|
var newConfig = null;
|
|
var euRex = /^EU\d{1,2}$/;
|
|
|
|
switch(true) {
|
|
case config.configLevel || 0 < 1:
|
|
// Original CONFIG format from 0.20 and earlier.
|
|
newConfig = B5500Util.deepCopy(B5500DiskStorageConfig.prototype.storageConfig);
|
|
newConfig.configLevel = 1;
|
|
newConfig.storageName = storageName;
|
|
|
|
for (name in config) { // for each property in config
|
|
if (name.search(euRex) == 0) { // filter name for "EUn" or "EUnn"
|
|
eu = config[name];
|
|
if (eu.constructor === Number) { // An older-format EU structure -- convert it
|
|
eu = B5500Util.deepCopy(B5500DiskStorageConfig.prototype.defaultEUConfig);
|
|
eu.size = config[name];
|
|
eu.slow = false;
|
|
}
|
|
newConfig[name] = eu;
|
|
}
|
|
} // for name
|
|
break;
|
|
|
|
default:
|
|
newConfig = config;
|
|
} // switch true
|
|
|
|
if (newConfig.configLevel != this.dbConfigLevel) {
|
|
this.alertWin.alert("ERROR: Cannot normalize existing CONFIG\nlevel " +
|
|
newConfig.configLevel + " to current level " + this.dbConfigLevel);
|
|
}
|
|
|
|
return newConfig;
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.getStorageConfig = function getStorageConfig(storageName, successor) {
|
|
/* Attempts to retrieve the Disk Storage structure under "storageName".
|
|
If successful, calls the "successor" function passing the configuration object;
|
|
otherwise calls "successor" passing null. Displays alerts for any errors encountered */
|
|
var that = this;
|
|
|
|
function readConfig() {
|
|
/* Reads the named system configuration structure from the data base,
|
|
then calls the successor function with the configuration object */
|
|
var txn = that.db.transaction(that.storageConfigName);
|
|
|
|
txn.objectStore(that.storageConfigName).get(0).onsuccess = function(ev) {
|
|
that.storageConfig = that.normalizeStorageConfig(ev.target.result, storageName);
|
|
successor(that.storageConfig);
|
|
};
|
|
}
|
|
|
|
function onOpenFailure(ev) {
|
|
that.storageConfig = null;
|
|
that.alertWin.alert("getStorageConfig cannot open \"" + storageName +
|
|
"\" data base:\n" + ev.target.error);
|
|
successor(that.storageConfig);
|
|
}
|
|
|
|
if (this.db && this.db.name == storageName) {
|
|
readConfig();
|
|
} else {
|
|
this.closeStorageDB();
|
|
this.openStorageDB(storageName, readConfig, onOpenFailure);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.putStorageConfig = function putStorageConfig(
|
|
config, successor) {
|
|
/* Attempts to store the structure "config" to the Disk Storage
|
|
data base. The data base name must be in config.storageName.
|
|
If successful, calls "successor" passing the success event */
|
|
var that = this;
|
|
|
|
function putConfig() {
|
|
var txn = that.db.transaction(that.storageConfigName, "readwrite");
|
|
|
|
txn.onerror = function(ev) {
|
|
that.alertWin.alert("putStorageConfig put error: " + ev.target.error);
|
|
}
|
|
|
|
txn.oncomplete = function(ev) {
|
|
that.storageConfig = config;
|
|
successor(ev);
|
|
};
|
|
|
|
txn.objectStore(that.storageConfigName).put(config, 0);
|
|
}
|
|
|
|
function onOpenFailure(ev) {
|
|
that.storageConfig = null;
|
|
that.alertWin.alert("putStorageConfig cannot open \"" + storageName +
|
|
"\" data base:\n" + ev.target.error);
|
|
}
|
|
|
|
if (this.db && this.db.name == config.storageName) {
|
|
putConfig();
|
|
} else {
|
|
this.closeStorageDB();
|
|
this.openStorageDB(config.storageName, putConfig, onOpenFailure);
|
|
}
|
|
};
|
|
|
|
|
|
/***********************************************************************
|
|
* Disk Storage Subsystem Configuration UI Support *
|
|
***********************************************************************/
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.loadStorageDialog = function loadStorageDialog(config) {
|
|
/* Loads the configuration UI window with the settings from "config" */
|
|
var enableBox;
|
|
var euName;
|
|
var eu;
|
|
var eux;
|
|
var sizeList;
|
|
var suCount;
|
|
var suSize;
|
|
var typeList;
|
|
var x;
|
|
|
|
if (!config) {
|
|
this.window.close();
|
|
} else {
|
|
this.$$("StorageName").textContent = config.storageName;
|
|
for (eux=0; eux<20; ++eux) {
|
|
euName = "EU" + eux.toString();
|
|
enableBox = this.$$(euName);
|
|
sizeList = this.$$(euName + "SU");
|
|
typeList = this.$$(euName + "Type");
|
|
if (!(euName in config)) {
|
|
suCount = suSize = 0;
|
|
enableBox.checked = false;
|
|
enableBox.disabled = false;
|
|
sizeList.selectedIndex = 0;
|
|
typeList.selectedIndex = 0;
|
|
typeList.options[0].disabled = false;
|
|
} else {
|
|
eu = config[euName];
|
|
suSize = (eu.slow ? 80000 : 40000);
|
|
suCount = Math.max(Math.min(Math.floor((eu.size+suSize-1)/suSize), 5), 1);
|
|
enableBox.checked = true;
|
|
enableBox.disabled = true;
|
|
sizeList.selectedIndex = suCount-1;
|
|
typeList.selectedIndex = (eu.slow ? 1 : 0);
|
|
typeList.options[0].disabled = eu.slow;
|
|
}
|
|
|
|
for (x=sizeList.length-1; x>=0; --x) {
|
|
sizeList.options[x].disabled = (x+1 < suCount);
|
|
}
|
|
} // for eux
|
|
|
|
this.$$("MessageArea").textContent = "Storage subsystem \"" + config.storageName + "\" loaded.";
|
|
this.window.focus();
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.saveStorageDialog = function saveStorageDialog(ev) {
|
|
/* Saves the configuration UI window settings to the System Config data base.
|
|
A new config object is cloned from the prototype, then the current configuration
|
|
is merged with the settings on the window form into the new object. If there
|
|
are no errors, the new object is stored in "CONFIG" and the window is closed */
|
|
var config = this.storageConfig; // current subsystem configuration object
|
|
var enableBox; // ref to EU check box
|
|
var fatal = false; // true if any errors
|
|
var eu; // current EU config structure
|
|
var euName; // current EU name
|
|
var euSize; // EU size in sectors from UI form
|
|
var euSlow; // true => Model-IB selected on UI form
|
|
var eux; // current EU index
|
|
var newConfig; // new configuration object
|
|
var sizeList; // ref to SUs list
|
|
var suCount; // number of SUs from UI form
|
|
var suSize; // new SU size based on euSlow
|
|
var that = this; // for closure use
|
|
var typeList; // ref to disk-type list
|
|
var upgradeNeeded = false; // true if schema upgrade needed
|
|
|
|
function finalFailure(ev) {
|
|
that.alertWin.alert("Disk Storage \"" + newConfig.storageName +
|
|
"\"\nconfiguration update failed: " + ev.target.error);
|
|
}
|
|
|
|
function finalSuccess(ev) {
|
|
that.alertWin.alert("Disk Storage \"" + newConfig.storageName +
|
|
"\"\nconfiguration updated successfully.");
|
|
that.window.close();
|
|
}
|
|
|
|
function putConfigSuccess(ev) {
|
|
/* Called back on a successful put of the new configuration structure.
|
|
Initiates a schema update if the new configuration requires one */
|
|
|
|
that.storageConfig = newConfig;
|
|
if (upgradeNeeded) {
|
|
that.modifyStorageSchema(finalSuccess, finalFailure);
|
|
} else {
|
|
finalSuccess(ev);
|
|
}
|
|
}
|
|
|
|
newConfig = B5500Util.deepCopy(B5500DiskStorageConfig.prototype.storageConfig);
|
|
newConfig.storageName = config.storageName;
|
|
|
|
for (eux=0; eux<20; ++eux) {
|
|
euName = "EU" + eux.toString();
|
|
enableBox = this.$$(euName);
|
|
sizeList = this.$$(euName + "SU");
|
|
typeList = this.$$(euName + "Type");
|
|
if (euName in config) {
|
|
eu = config[euName];
|
|
if (!enableBox.checked) {
|
|
fatal = true;
|
|
this.alertWin.alert("ERROR: Cannot remove " + euName + ".");
|
|
}
|
|
} else if (enableBox.checked) {
|
|
eu = B5500Util.deepCopy(B5500DiskStorageConfig.prototype.defaultEUConfig);
|
|
eu.size = 0;
|
|
eu.slow = false;
|
|
upgradeNeeded = true;
|
|
} else {
|
|
eu = null;
|
|
}
|
|
|
|
if (enableBox.checked) {
|
|
euSlow = typeList.selectedIndex == 1;
|
|
suSize = (euSlow ? 80000 : 40000);
|
|
suCount = sizeList.selectedIndex + 1;
|
|
euSize = suSize*suCount;
|
|
if (eu.slow && !euSlow) {
|
|
fatal = true;
|
|
this.alertWin.alert("ERROR: Cannot change " + euName +
|
|
" from\nModel-IB (slow) to Model-I disk.");
|
|
} else if (euSize < eu.size) {
|
|
fatal = true;
|
|
this.alertWin.alert("ERROR: Cannot reduce size of " + euName + ".");
|
|
} else {
|
|
eu.size = euSize;
|
|
eu.slow = euSlow;
|
|
}
|
|
}
|
|
|
|
if (eu) {
|
|
newConfig[euName] = eu;
|
|
}
|
|
} // for eux
|
|
|
|
if (!fatal) {
|
|
this.$$("MessageArea").textContent = "Saving Disk Storage configuration \"" +
|
|
newConfig.storageName + "\".";
|
|
this.putStorageConfig(newConfig, putConfigSuccess);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.deleteStorageDialog = function deleteStorageDialog(storageName) {
|
|
/* Initiates deletion of the currently-selected system configuration */
|
|
var that = this;
|
|
|
|
function deleteFailed(ev) {
|
|
that.alertWin.alert("Deletion of data base \"" + storageName +
|
|
"\" failed: " + ev.target.error);
|
|
}
|
|
function deleteOK(ev) {
|
|
that.window.close();
|
|
}
|
|
|
|
this.deleteStorageDB(storageName, deleteOK, deleteFailed);
|
|
};
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.closeStorageUI = function closeStorageUI() {
|
|
/* Closes this Storage Subsystem update dialog */
|
|
|
|
this.closeStorageDB();
|
|
this.alertWin = window; // revert alerts to the global window
|
|
if (this.window) {
|
|
if (!this.window.closed) {
|
|
this.window.close();
|
|
}
|
|
this.window = null;
|
|
this.doc = null;
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500DiskStorageConfig.prototype.openStorageUI = function openStorageUI(storageName) {
|
|
/* Opens this Storage Subsystem update dialog and displays the disk
|
|
subsystem configuration for "storageName" */
|
|
var that = this;
|
|
|
|
function storageUI_Open(ev) {
|
|
this.doc = ev.target;
|
|
this.window = this.doc.defaultView;
|
|
this.alertWin = this.window;
|
|
|
|
this.window.moveTo(screen.availWidth-this.window.outerWidth-80,
|
|
(screen.availHeight-this.window.outerHeight)/2);
|
|
this.window.focus();
|
|
|
|
this.getStorageConfig(storageName,
|
|
this.loadStorageDialog.bind(this));
|
|
this.$$("StorageDeleteBtn").addEventListener("click", function(ev) {
|
|
that.deleteStorageDialog(storageName);
|
|
});
|
|
this.$$("SaveBtn").addEventListener("click",
|
|
this.saveStorageDialog.bind(this));
|
|
this.$$("CancelBtn").addEventListener("click", function(ev) {
|
|
that.window.close();
|
|
});
|
|
this.window.addEventListener("unload",
|
|
this.closeStorageUI.bind(this), false);
|
|
}
|
|
|
|
B5500Util.openPopup(window, "../webUI/B5500DiskStorageConfig.html", storageName+"_Config",
|
|
"location=no,scrollbars,resizable,width=560,height=480",
|
|
this, storageUI_Open);
|
|
};
|