import { Guid } from "guid-typescript" ; import * as path from "path" ; import * as vscode from "vscode" ; import { ArgumentEmptyOrNullError } from "../common/Error/OperationFailedErrors/ArgumentEmptyOrNullError" ; import { OperationCanceledError } from "../common/Error/OperationCanceledError" ; import { FileNames , OperationType , PlatformType , ScaffoldType , TemplateTag } from "../constants" ; import { DigitalTwinConstants } from "../DigitalTwin/DigitalTwinConstants" ; import { FileUtility } from "../FileUtility" ; import { TelemetryContext } from "../telemetry" ; import * as utils from "../utils" ; import { ComponentType } from "./Interfaces/Component" ; import { Device , DeviceType } from "./Interfaces/Device" ; import { ProjectTemplate , TemplateFileInfo , TemplatesType } from "./Interfaces/ProjectTemplate" ; import { RemoteExtension } from "./RemoteExtension" ; import { DirectoryNotFoundError } from "../common/Error/OperationFailedErrors/DirectoryNotFoundError" ; const constants = { configFile : "config.json" , compileTaskName : "default compile script" } ; export abstract class ContainerDeviceBase implements Device { protected componentId : string ; get id ( ) : string { return this . componentId ; } protected deviceType : DeviceType ; protected componentType : ComponentType ; protected projectFolder : string ; protected channel : vscode . OutputChannel ; protected extensionContext : vscode . ExtensionContext ; protected telemetryContext : TelemetryContext ; protected outputPath : string ; name = "container base" ; constructor ( context , projectPath , channel , telemetryContext , deviceType , protected templateFilesInfo = [ ] ) { this . deviceType = deviceType ; this . componentType = ComponentType . Device ; this . channel = channel ; this . componentId = Guid . create ( ) . toString ( ) ; this . extensionContext = context ; this . projectFolder = projectPath ; this . outputPath = path . join ( this . projectFolder , FileNames . outputPathName ) ; this . telemetryContext = telemetryContext ; } getDeviceType ( ) { return this . deviceType ; } getComponentType ( ) { return this . componentType ; } async checkPrerequisites ( ) { } async load ( ) { const scaffoldType = ScaffoldType . Workspace ; const operation = "load container device" ; this . validateProjectFolder ( operation , scaffoldType ) ; } async create ( ) { const createTimeScaffoldType = ScaffoldType . Local ; const operation = "create container device" ; this . validateProjectFolder ( operation , createTimeScaffoldType ) ; await this . generateTemplateFiles ( createTimeScaffoldType , this . projectFolder , this . templateFilesInfo ) ; await this . configDeviceEnvironment ( this . projectFolder , createTimeScaffoldType ) ; } async generateTemplateFiles ( type , projectPath , templateFilesInfo ) { if ( ! templateFilesInfo ) { throw new ArgumentEmptyOrNullError ( "generate template files" , "template files" ) ; } if ( ! projectPath ) { throw new ArgumentEmptyOrNullError ( "generate template files" , "project path" ) ; } for ( const fileInfo of templateFilesInfo ) { if ( fileInfo . fileName === DigitalTwinConstants . cmakeListsFileName ) { const pattern = /{project_name}/g ; const projectName = path . basename ( projectPath ) ; if ( fileInfo . fileContent ) { fileInfo . fileContent = fileInfo . fileContent . replace ( pattern , projectName ) ; } } await utils . generateTemplateFile ( this . projectFolder , type , fileInfo ) ; } return true ; } async compile ( ) { const isRemote = RemoteExtension . isRemote ( this . extensionContext ) ; if ( ! isRemote ) { await utils . askAndOpenInRemote ( OperationType . Compile , this . telemetryContext ) ; return false ; } await utils . fetchAndExecuteTask ( this . extensionContext , this . channel , this . telemetryContext , this . projectFolder , OperationType . Compile , PlatformType . EmbeddedLinux , constants . compileTaskName ) ; return true ; } abstract async upload ( ) ; abstract async configDeviceSettings ( ) ; async configDeviceEnvironment ( projectPath , scaffoldType ) { if ( ! projectPath ) { throw new ArgumentEmptyOrNullError ( "configure device environment" , "project path" , "Please open the folder and initialize project again." ) ; } const templateJson = await utils . getTemplateJson ( this . extensionContext , scaffoldType ) ; const containerSelection = await this . selectContainer ( templateJson ) ; if ( ! containerSelection ) { throw new OperationCanceledError ( `Container selection cancelled.` ) ; } const templateName = containerSelection . label ; const templateFilesInfo = await utils . getEnvTemplateFilesAndAskOverwrite ( this . extensionContext , this . projectFolder , scaffoldType , templateName ) ; for ( const fileInfo of templateFilesInfo ) { if ( fileInfo . fileName === "tasks.json" ) { const pattern = "${project_name}" ; const projectName = path . basename ( projectPath ) ; if ( fileInfo . fileContent ) { fileInfo . fileContent = fileInfo . fileContent . replace ( pattern , projectName ) ; } } await utils . generateTemplateFile ( projectPath , scaffoldType , fileInfo ) ; } const message = "Container device configuration done." ; utils . channelShowAndAppendLine ( this . channel , message ) ; } private async selectContainer ( templateListJson ) { const containerTemplates = templateListJson . templates . filter ( ( template ) => { return template . tag === TemplateTag . DevelopmentEnvironment && template . platform === PlatformType . EmbeddedLinux ; } ) ; const containerList = [ ] ; containerTemplates . forEach ( ( container ) => { containerList . push ( { label : container . name , detail : container . detail } ) ; } ) ; const containerSelection = await vscode . window . showQuickPick ( containerList , { ignoreFocusOut : true , matchOnDescription : true , matchOnDetail : true , placeHolder : "Select a toolchain container for your device platform" } ) ; return containerSelection ; } param operation param scaffoldType async validateProjectFolder ( operation , scaffoldType ) { if ( ! ( await FileUtility . directoryExists ( scaffoldType , this . projectFolder ) ) ) { throw new DirectoryNotFoundError ( operation , `project folder ${ this . projectFolder }` , "Please initialize the project first." ) ; } } }