diff --git a/.deepsource.toml b/.deepsource.toml index 87c5357a..02492c7b 100644 --- a/.deepsource.toml +++ b/.deepsource.toml @@ -8,7 +8,8 @@ test_patterns = [ exclude_patterns = [ "lib/**", "orbit_bundle.js", - "public/**" + "public/**", + "cc/**" ] [[analyzers]] diff --git a/src/app/core/device.ts b/src/app/core/device.ts index e0cc7090..864a6895 100644 --- a/src/app/core/device.ts +++ b/src/app/core/device.ts @@ -20,6 +20,7 @@ import * as IOUtils from "../utils/ioUtils"; import DeviceUtils from "@/app/utils/deviceUtils"; import { ComponentAPI } from "@/componentAPI"; import MapUtils from "../utils/mapUtils"; +import { SerializationError } from "../utils/exportUtils"; /** * The Device stores information about a design. @@ -469,10 +470,20 @@ export default class Device { * @returns {Array} * @memberof Device */ - __componentsToInterchangeV1(): Array { + __componentsToInterchangeV1(errorList: Array): Array { let output: Array = []; for (let i in this.__components) { - output.push(this.__components[i].toInterchangeV1()); + try { + output.push(this.__components[i].toInterchangeV1()); + } + catch (e) { + console.error(e); + errorList.push(new SerializationError( + e.message, + `Component ${this.__components[i].id} could not be converted to InterchangeV1`, + JSON.stringify(this.__components[i]), + )); + } } return output; } @@ -481,28 +492,46 @@ export default class Device { * @returns {Array} Returns an array with the connections * @memberof Device */ - __connectionToInterchangeV1(): Array { + __connectionToInterchangeV1(errorList: Array): Array { let output: Array = []; for (let i in this.__connections) { - output.push(this.__connections[i].toInterchangeV1()); + try { + output.push(this.__connections[i].toInterchangeV1()); + } catch (e) { + console.error(e); + errorList.push(new SerializationError( + e.message, + `Connection : ${this.__connections[i].id} could not be converted to InterchangeV1`, + JSON.stringify(this.__connections[i]), + )); + } } return output; } - __valvesToInterchangeV1(): Array { + __valvesToInterchangeV1(errorList: Array): Array { let output: Array = []; - this.__valveMap.forEach((target, valve) => { - let valve_type = this.__valveTypeMap.get(valve); + this.__valveMap.forEach((target, valveID) => { + let valve_type = this.__valveTypeMap.get(valveID); if(valve_type === undefined) { - console.error("Valve type not found for valve: " + valve + " , setting default to NORMALLY_OPEN"); + console.warn("Valve type not found for valve: " + valveID + " , setting default to NORMALLY_OPEN"); valve_type = ValveType.NORMALLY_OPEN; } - output.push({ - componentid: valve, - connectionid: target, - type: valve_type, - params: {} - }); + try { + output.push({ + componentid: valveID, + connectionid: target, + type: valve_type, + params: {} + }); + } catch (e) { + console.error(e); + errorList.push(new SerializationError( + e.message, + `Valve : ${valveID} could not be converted to InterchangeV1`, + JSON.stringify(valveID), + )); + } }); return output; } @@ -524,10 +553,19 @@ export default class Device { * @return {Array} Returns an array with the layers * @memberof Device */ - __layersToInterchangeV1(): Array { + __layersToInterchangeV1(errorList: Array): Array { const output: Array = []; for (const i in this.__layers) { - output.push(this.__layers[i].toInterchangeV1()); + try { + output.push(this.__layers[i].toInterchangeV1()); + } catch (e) { + console.error(e); + errorList.push(new SerializationError( + e.message, + `Layer ${this.__layers[i].id} could not be converted to InterchangeV1`, + JSON.stringify(this.__layers[i]) + )); + } } return output; } @@ -579,7 +617,7 @@ export default class Device { * @returns {Device} Returns an Device object in Interchange V1 format * @memberof Device */ - toInterchangeV1(): DeviceInterchangeV1 { + toInterchangeV1(errorList: Array): DeviceInterchangeV1 { let output: DeviceInterchangeV1 = { name: this.__name, params: { @@ -587,17 +625,17 @@ export default class Device { length: this.getYSpan() }, //TODO: Use this to dynamically create enough layers to scroll through - layers: this.__layersToInterchangeV1(), - components: this.__componentsToInterchangeV1(), - connections: this.__connectionToInterchangeV1(), - valves: this.__valvesToInterchangeV1(), + layers: this.__layersToInterchangeV1(errorList), + components: this.__componentsToInterchangeV1(errorList), + connections: this.__connectionToInterchangeV1(errorList), + valves: this.__valvesToInterchangeV1(errorList), version: "1", groups: this.__groupsToJSON() }; return output; } - toInterchangeV1_1(): DeviceInterchangeV1_1 { + toInterchangeV1_1(errorList: Array): DeviceInterchangeV1_1 { let output: DeviceInterchangeV1_1 = { name: this.__name, params: { @@ -605,9 +643,9 @@ export default class Device { length: this.getYSpan() }, //TODO: Use this to dynamically create enough layers to scroll through - layers: this.__layersToInterchangeV1(), - components: this.__componentsToInterchangeV1(), - connections: this.__connectionToInterchangeV1(), + layers: this.__layersToInterchangeV1(errorList), + components: this.__componentsToInterchangeV1(errorList), + connections: this.__connectionToInterchangeV1(errorList), version: "1.1", groups: this.__groupsToJSON() }; diff --git a/src/app/manufacturing/cncGenerator.ts b/src/app/manufacturing/cncGenerator.ts index 02bac6ae..62f2fec5 100644 --- a/src/app/manufacturing/cncGenerator.ts +++ b/src/app/manufacturing/cncGenerator.ts @@ -15,7 +15,7 @@ import { DFMType } from "./manufacturingInfo"; export default class CNCGenerator { __device: Device; __viewManagerDelegate: viewManager; - __svgData: Map; + __svgData: Map; /** * Default Constructor of GNCGenerator object. @@ -34,7 +34,7 @@ export default class CNCGenerator { * @returns {} * @memberof CNCGenerator */ - getSVGOutputs(): Map { + getSVGOutputs(): Map { return this.__svgData; } diff --git a/src/app/manufacturing/laserCuttingGenerator.ts b/src/app/manufacturing/laserCuttingGenerator.ts index 71d75b6d..9d6a5729 100644 --- a/src/app/manufacturing/laserCuttingGenerator.ts +++ b/src/app/manufacturing/laserCuttingGenerator.ts @@ -34,7 +34,7 @@ export default class LaserCuttingGenerator { * @returns Returns the SVG data * @memberof LaserCuttingGenerator */ - getSVGOutputs(): Map { + getSVGOutputs(): Map { return this.__svgData; } diff --git a/src/app/manufacturing/manufacturingLayer.ts b/src/app/manufacturing/manufacturingLayer.ts index 756eb9dd..ca230d17 100644 --- a/src/app/manufacturing/manufacturingLayer.ts +++ b/src/app/manufacturing/manufacturingLayer.ts @@ -11,7 +11,7 @@ import { ToolPaperObject } from "../core/init"; */ export default class ManufacturingLayer { __features: Array; - __name: String; + __name: string; __paperGroup: paper.Group; __flip: boolean; @@ -19,7 +19,7 @@ export default class ManufacturingLayer { * Default Constructor for the Manufacturing Layer * @param {String} name Name of the field */ - constructor(name: String, flip = false) { + constructor(name: string, flip = false) { this.__features = []; this.__name = name; this.__paperGroup = new paper.Group(); @@ -31,7 +31,7 @@ export default class ManufacturingLayer { * @return {String} Returns the name of the field * @memberof ManufacturingLayer */ - get name(): String { + get name(): string { return this.__name; } diff --git a/src/app/utils/exportUtils.ts b/src/app/utils/exportUtils.ts index 8ac876a0..7f4b729e 100644 --- a/src/app/utils/exportUtils.ts +++ b/src/app/utils/exportUtils.ts @@ -16,6 +16,64 @@ import { import ConnectionTarget from "../core/connectionTarget"; import GeometryElement from "../core/geometryElement"; +export class SerializationError { + /** + * Error message for the user. + * + * @type {string} + * @memberof SerializationError + */ + public message: string; + + /** + * The element that caused the error. + * TBD on how ot use this in the future. + * @type {*} + * @memberof SerializationError + */ + public element: string; + + /** + * The JSON data that was being processed when the error occurred. + * + * @type {string} + * @memberof SerializationError + */ + public jsonData: string; + + /** + * Creates an instance of SerializationError. + * @param {string} message + * @param {string} element + * @param {string} jsonData + * @memberof SerializationError + */ + constructor(message: string, element: string, jsonData: string) { + this.message = message; + this.element = element; + this.jsonData = jsonData; + } + + /** + * Converts the error to a string. + * suitable for display to the user. or in a log file. + * + * @returns {string} + * @memberof SerializationError + */ + toText(): string { + let ret = `Error: ${this.message}\n`; + ret += `Element: ${this.element}\n`; + ret += "JSON Data:\n"; + ret += "\`\`\`\n"; + ret += this.jsonData; + ret += "\n\`\`\`\n"; + + return ret; + } +} + + export default class ExportUtils { /** @@ -26,7 +84,7 @@ export default class ExportUtils { * @returns {InterchangeV1_2} * @memberof ExportUtils */ - static toInterchangeV1_2(viewManagerDelegate: ViewManager): InterchangeV1_2 { + static toInterchangeV1_2(viewManagerDelegate: ViewManager, errorList:SerializationError[]): InterchangeV1_2 { if(viewManagerDelegate.currentDevice === null) { throw new Error("No device selected"); } @@ -35,7 +93,7 @@ export default class ExportUtils { for (let i = 0; i < viewManagerDelegate.renderLayers.length; i++) { renderLayers.push(viewManagerDelegate.renderLayers[i].toInterchangeV1()); } - const device = viewManagerDelegate.currentDevice.toInterchangeV1(); + const device = viewManagerDelegate.currentDevice.toInterchangeV1(errorList); const valvemap = {}; const valvetypemap = {}; diff --git a/src/app/view/paperView.ts b/src/app/view/paperView.ts index d290eac9..37ca54ff 100644 --- a/src/app/view/paperView.ts +++ b/src/app/view/paperView.ts @@ -461,7 +461,7 @@ export default class PaperView { * @returns {void} * @memberof PaperView */ - setResizeFunction(func: Function | null): void { + setResizeFunction(func: () => void | null): void { if (this.canvas === null) { throw new Error("Canvas is null"); } diff --git a/src/app/view/viewManager.ts b/src/app/view/viewManager.ts index 4254ae5c..39979ab0 100644 --- a/src/app/view/viewManager.ts +++ b/src/app/view/viewManager.ts @@ -41,7 +41,7 @@ import { ComponentAPI } from "@/componentAPI"; import RenderLayer from "@/app/view/renderLayer"; import LoadUtils from "@/app/utils/loadUtils"; -import ExportUtils from "@/app/utils/exportUtils"; +import ExportUtils, { SerializationError } from "@/app/utils/exportUtils"; import { LogicalLayerType, InterchangeV1_2, ValveType } from "@/app/core/init"; import { Point } from "@/app/core/init"; @@ -1374,7 +1374,10 @@ export default class ViewManager { saveDeviceState(): void { console.log("Saving to stack"); - const save = JSON.stringify(Registry.currentDevice?.toInterchangeV1()); + // TODO - Future versions of the software should be using the saving + // the device as a set of atomic operations and not as the json data + // unless we are using it as a refernce. + const save = JSON.stringify(Registry.currentDevice?.toInterchangeV1([])); this.undoStack.pushDesign(save); } @@ -1546,8 +1549,8 @@ export default class ViewManager { * @returns {void} * @memberof ViewManager */ - generateExportJSON(): InterchangeV1_2 { - const json = ExportUtils.toInterchangeV1_2(this); + generateExportJSON(errorList:Array = []): InterchangeV1_2 { + const json = ExportUtils.toInterchangeV1_2(this, errorList); // const json = this.currentDevice.toInterchangeV1_1(); // json.customComponents = this.customComponentManager.toJSON(); return json; @@ -1714,10 +1717,23 @@ export default class ViewManager { if(this.currentDevice === null){ throw new Error("No device loaded"); } - let json = new Blob([JSON.stringify(this.generateExportJSON())], { + const errorList: Array = []; + const json = new Blob([JSON.stringify(this.generateExportJSON(errorList))], { type: "application/json" }); saveAs(json, this.currentDevice.name + ".json"); + + if (errorList.length > 0) { + // Concatenate all the errors into a single string with newlines and save it as a file + let errorString = ""; + for (const error of errorList) { + errorString += error.toText() + "\n\n"; + } + const errorBlob = new Blob([errorString], { + type: "text/plain" + }); + saveAs(errorBlob, this.currentDevice.name + "_errors.txt"); + } } createNewDevice(name: string): void { diff --git a/src/components/ManufacturingPanel.vue b/src/components/ManufacturingPanel.vue index 8b9bf18b..d358a541 100644 --- a/src/components/ManufacturingPanel.vue +++ b/src/components/ManufacturingPanel.vue @@ -88,10 +88,7 @@ export default { }, methods: { downloadJSON() { - let json = new Blob([JSON.stringify(this.viewManagerRef.generateExportJSON())], { - type: "application/json" - }); - saveAs(json, Registry.currentDevice.name + ".json"); + this.viewManagerRef.downloadJSON(); }, downloadSVG() { let svgs = this.viewManagerRef.layersToSVGStrings();