diff --git a/libs/core/core/hooks/use-msc-device-detached-effect.ts b/libs/core/core/hooks/use-msc-device-detached-effect.ts index 0b9d38e3e6..bbc452f0db 100644 --- a/libs/core/core/hooks/use-msc-device-detached-effect.ts +++ b/libs/core/core/hooks/use-msc-device-detached-effect.ts @@ -15,6 +15,8 @@ import { abortMscFlashing, FlashingProcessState, selectIsFlashingInActivePhases, + selectIsFlashingInState, + setMscFlashingInitialState, } from "msc-flash-harmony" import { Dispatch } from "Core/__deprecated__/renderer/store" @@ -35,6 +37,10 @@ export const useMscDeviceDetachedEffect = () => { const useHandleDevicesDetached = () => { const dispatch = useDispatch() const flashingInActivePhases = useSelector(selectIsFlashingInActivePhases) + const flashingInWaitingForBackButtonState = useSelector( + selectIsFlashingInState(FlashingProcessState.WaitingForBackButton) + ) + return useCallback( (deviceDetachedEvents: DeviceBaseProperties[]) => { const mscEvents = deviceDetachedEvents.filter( @@ -45,11 +51,16 @@ const useHandleDevicesDetached = () => { return } + if (flashingInWaitingForBackButtonState) { + dispatch(setMscFlashingInitialState()) + return + } + const reason = flashingInActivePhases ? FlashingProcessState.Failed : undefined dispatch(abortMscFlashing({ reason })) }, - [dispatch, flashingInActivePhases] + [dispatch, flashingInActivePhases, flashingInWaitingForBackButtonState] ) } diff --git a/libs/generic-view/store/src/lib/action-names.ts b/libs/generic-view/store/src/lib/action-names.ts index a7743e3a38..8c8d258cad 100644 --- a/libs/generic-view/store/src/lib/action-names.ts +++ b/libs/generic-view/store/src/lib/action-names.ts @@ -93,6 +93,7 @@ export enum ActionName { MscFlashingSetProcessState = "msc-flashing/set-process-state", MscFlashingSetAbort = "msc-flashing/set-abort", MscFlashingAbort = "msc-flashing/abort", + MscFlashingSetInitialState = "msc-flashing/set-initial-state", GetEntitiesConfig = "entities/get-entities-config", SetEntitiesConfig = "entities/set-entities-config", diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/actions/actions.ts b/libs/msc-flash/msc-flash-harmony/src/lib/actions/actions.ts index 92d03fd3a9..b703d50862 100644 --- a/libs/msc-flash/msc-flash-harmony/src/lib/actions/actions.ts +++ b/libs/msc-flash/msc-flash-harmony/src/lib/actions/actions.ts @@ -9,3 +9,7 @@ import { ActionName } from "generic-view/store" export const setMscFlashingAbort = createAction( ActionName.MscFlashingSetAbort ) + +export const setMscFlashingInitialState = createAction( + ActionName.MscFlashingSetInitialState +) diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/reducers/flashing.reducer.ts b/libs/msc-flash/msc-flash-harmony/src/lib/reducers/flashing.reducer.ts index f5ad29b599..8fb794cee7 100644 --- a/libs/msc-flash/msc-flash-harmony/src/lib/reducers/flashing.reducer.ts +++ b/libs/msc-flash/msc-flash-harmony/src/lib/reducers/flashing.reducer.ts @@ -5,7 +5,11 @@ import { createReducer } from "@reduxjs/toolkit" import { FlashingState } from "./flashing.interface" -import { getMscFlashingFilesDetails, setFlashingProcessState } from "../actions" +import { + getMscFlashingFilesDetails, + setFlashingProcessState, + setMscFlashingInitialState, +} from "../actions" import { FlashingProcessState } from "../constants" import { setMscFlashingAbort } from "../actions/actions" @@ -28,5 +32,8 @@ export const flashingReducer = createReducer( .addCase(setMscFlashingAbort, (state, action) => { state.abortController = action.payload }) + .addCase(setMscFlashingInitialState, () => { + return { ...initialState } + }) } ) diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/selectors/index.ts b/libs/msc-flash/msc-flash-harmony/src/lib/selectors/index.ts index 313272bf24..e523005240 100644 --- a/libs/msc-flash/msc-flash-harmony/src/lib/selectors/index.ts +++ b/libs/msc-flash/msc-flash-harmony/src/lib/selectors/index.ts @@ -7,3 +7,4 @@ export * from "./select-flashing-state" export * from "./select-flashing-abort-controller" export * from "./select-flashing-process-state" export * from "./select-is-flashing-in-active-phases" +export * from "./select-is-flashing-in-state" diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/selectors/select-is-flashing-in-state.ts b/libs/msc-flash/msc-flash-harmony/src/lib/selectors/select-is-flashing-in-state.ts new file mode 100644 index 0000000000..fb799f00c0 --- /dev/null +++ b/libs/msc-flash/msc-flash-harmony/src/lib/selectors/select-is-flashing-in-state.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +import { createSelector } from "@reduxjs/toolkit" +import { FlashingProcessState } from "../constants" +import { selectFlashingProcessState } from "./select-flashing-process-state" + +export const selectIsFlashingInState = (targetState: FlashingProcessState) => + createSelector( + selectFlashingProcessState, + (flashingProcessState) => flashingProcessState === targetState + ) diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/device-flash.factory.ts b/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/device-flash.factory.ts index d5d537beb2..c4f22c9f8f 100644 --- a/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/device-flash.factory.ts +++ b/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/device-flash.factory.ts @@ -6,15 +6,20 @@ import IDeviceFlash from "./device-flash.interface" import LinuxDeviceFlashService from "./linux/linux-device-flash.service" import MacDeviceFlashService from "./macos/macos-device-flash-service" +import WindowsDeviceFlashService from "./windows/windows-device-flash.service" class DeviceFlashFactory { - static createDeviceFlashService(temporaryDirectoryPath: string): IDeviceFlash { + static createDeviceFlashService( + temporaryDirectoryPath: string + ): IDeviceFlash { const platform = process.platform if (platform === "linux") { return new LinuxDeviceFlashService() } else if (platform === "darwin") { return new MacDeviceFlashService(temporaryDirectoryPath) + } else if (platform === "win32") { + return new WindowsDeviceFlashService() } else { throw new Error(`Unsupported platform: ${platform}`) } diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/windows/windows-device-flash.service.ts b/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/windows/windows-device-flash.service.ts new file mode 100644 index 0000000000..79bf4d0ac2 --- /dev/null +++ b/libs/msc-flash/msc-flash-harmony/src/lib/services/device-flash/windows/windows-device-flash.service.ts @@ -0,0 +1,71 @@ +/** + * Copyright (c) Mudita sp. z o.o. All rights reserved. + * For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md + */ + +import { + execCommandWithSudo, + execPromise, + splitPathToDirNameAndBaseName, +} from "shared/utils" +import IDeviceFlash from "../device-flash.interface" + +interface DiskInformation { + DiskNumber: number + FriendlyName: string + Size: string + OperationalStatus: string +} + +class WindowsDeviceFlashService implements IDeviceFlash { + async findDeviceByDeviceName(deviceName: string): Promise { + console.log( + `Searching for the device with the friendly name: ${deviceName}` + ) + + const getDiskResult = await execPromise( + `powershell.exe -Command "Get-Disk | Where-Object { $_.FriendlyName -eq '${deviceName}' }| ConvertTo-Json"` + ) + + if (!getDiskResult) { + throw new Error(`Disk not found for friendly name: ${deviceName}`) + } + + const diskInformation: DiskInformation = JSON.parse(getDiskResult) + + console.log( + `Disk information retrieved successfully: Disk Number - ${diskInformation.DiskNumber}, Size - ${diskInformation.Size} bytes, Status - ${diskInformation.OperationalStatus}` + ) + + return String(diskInformation.DiskNumber) + } + + async execute( + device: string, + imagePath: string, + scriptPath: string + ): Promise { + await this.flashDevice(device, imagePath, scriptPath) + console.log("Flash process completed successfully") + } + + private async flashDevice( + device: string, + imagePath: string, + scriptPath: string + ): Promise { + const [path, scriptBasename] = splitPathToDirNameAndBaseName(scriptPath) + const [, imageBasename] = splitPathToDirNameAndBaseName(imagePath) + const command = `cd ${path} && powershell.exe -ExecutionPolicy Bypass -File ${scriptBasename} -file ${imageBasename} -diskid ${device} -force` + + try { + await execCommandWithSudo(command) + } catch (error) { + throw new Error( + `An error occurred during flashing: ${JSON.stringify(error)}` + ) + } + } +} + +export default WindowsDeviceFlashService diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/services/flash-msc-device.service.ts b/libs/msc-flash/msc-flash-harmony/src/lib/services/flash-msc-device.service.ts index fbe6e7626a..9d701f4798 100644 --- a/libs/msc-flash/msc-flash-harmony/src/lib/services/flash-msc-device.service.ts +++ b/libs/msc-flash/msc-flash-harmony/src/lib/services/flash-msc-device.service.ts @@ -5,22 +5,23 @@ import path from "path" import { Dispatch, ReduxRootState } from "Core/__deprecated__/renderer/store" +import logger from "Core/__deprecated__/main/utils/logger" +import getAppSettingsMain from "Core/__deprecated__/main/functions/get-app-settings" +import { RELEASE_SPACE } from "Core/update/constants/release-space.constant" import { FlashingProcessState, SupportedPlatform, Product } from "../constants" import { setFlashingProcessState } from "../actions/set-flashing-process-state.action" import { getMscFlashingFilesDetails } from "../actions/get-msc-flashing-files-details.action" import { MscFlashDetails } from "../dto" import { downloadFlashingFileRequest } from "../requests" +import { setMscFlashingAbort } from "../actions/actions" +import { selectIsFlashingInActivePhases } from "../selectors" import { unpackFlashingImageService } from "./unpack-flashing-image" import DeviceFlashFactory from "./device-flash/device-flash.factory" -import getAppSettingsMain from "Core/__deprecated__/main/functions/get-app-settings" - -const IMAGE_FILE_NAME = "BellHybrid.img" -import { RELEASE_SPACE } from "Core/update/constants/release-space.constant" import MacDeviceFlashService from "./device-flash/macos/macos-device-flash-service" +import IDeviceFlash from "./device-flash/device-flash.interface" import { removeDownloadedMscFiles } from "./remove-downloaded-msc-files.service" -import { setMscFlashingAbort } from "../actions/actions" -import { selectIsFlashingInActivePhases } from "../selectors" -import logger from "Core/__deprecated__/main/utils/logger" + +const IMAGE_FILE_NAME = "BellHybrid.img" export const flashMscDeviceService = () => async (dispatch: Dispatch, getState: () => ReduxRootState) => { @@ -148,34 +149,62 @@ const startFlashingProcess = async ( const deviceFlash = DeviceFlashFactory.createDeviceFlashService(osDownloadLocation) + const deviceName = getDeviceName() + const device = await deviceFlash.findDeviceByDeviceName(deviceName) - const device = await deviceFlash.findDeviceByDeviceName("HARMONY") - - const flashingScriptName = flashingFiles - ? flashingFiles.scripts[0].name - : "" - - const imageFilePath = path.join(osDownloadLocation, IMAGE_FILE_NAME) - const scriptFilePath = path.join(osDownloadLocation, flashingScriptName) + const { imageFilePath, scriptFilePath } = buildFlashingFilePaths( + flashingFiles, + osDownloadLocation + ) await deviceFlash.execute(device, imageFilePath, scriptFilePath) + if (signal.aborted) { return } - if (deviceFlash instanceof MacDeviceFlashService) { - dispatch(setFlashingProcessState(FlashingProcessState.TerminalOpened)) - - await deviceFlash.waitForFlashCompletion({ signal }) - } + await handlePostExecutingTasks(deviceFlash, dispatch, signal) if (signal.aborted) { return } - dispatch(setFlashingProcessState(FlashingProcessState.Restarting)) await removeDownloadedMscFiles() } catch (error) { throw new Error(`Flash process failed with error: ${JSON.stringify(error)}`) } } + +const getDeviceName = () => { + return process.platform === "win32" ? "MUDITA HARMONY MSC" : "HARMONY" +} + +const buildFlashingFilePaths = ( + flashingFiles: MscFlashDetails | undefined, + osDownloadLocation: string +) => { + const flashingFilesScripts = + process.platform === "win32" + ? flashingFiles?.scripts[1] + : flashingFiles?.scripts[0] + const flashingScriptName = flashingFilesScripts?.name ?? "" + const imageFilePath = path.join(osDownloadLocation, IMAGE_FILE_NAME) + const scriptFilePath = path.join(osDownloadLocation, flashingScriptName) + + return { imageFilePath, scriptFilePath } +} + +const handlePostExecutingTasks = async ( + deviceFlash: IDeviceFlash, + dispatch: Dispatch, + signal: AbortSignal +) => { + if (deviceFlash instanceof MacDeviceFlashService) { + dispatch(setFlashingProcessState(FlashingProcessState.TerminalOpened)) + await deviceFlash.waitForFlashCompletion({ signal }) + } else if (process.platform === "win32") { + dispatch(setFlashingProcessState(FlashingProcessState.WaitingForBackButton)) + } else { + dispatch(setFlashingProcessState(FlashingProcessState.Restarting)) + } +} diff --git a/libs/msc-flash/msc-flash-harmony/src/lib/services/unpack-flashing-image.ts b/libs/msc-flash/msc-flash-harmony/src/lib/services/unpack-flashing-image.ts index 084b51e70f..75f6e9a5a1 100644 --- a/libs/msc-flash/msc-flash-harmony/src/lib/services/unpack-flashing-image.ts +++ b/libs/msc-flash/msc-flash-harmony/src/lib/services/unpack-flashing-image.ts @@ -18,6 +18,10 @@ export const unpackFlashingImageService = async ( const imageFilePath = path.join(osDownloadLocation, fileName) command = `tar -xf "${imageFilePath}" -C "${osDownloadLocation}"` } + if (process.platform === "win32") { + const imageFilePath = path.join(osDownloadLocation, fileName) + command = `tar -xzvf "${imageFilePath}" -C "${osDownloadLocation}"` + } try { await execPromise(command)