| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- import React, { useCallback, useState, useEffect } from "react";
- /*
- ____ _ ____ _ _
- | _ \ ___ __| |_ ___ __ / ___|| |_ __ _| |_ ___ ___
- | |_) / _ \/ _` | | | \ \/ / \___ \| __/ _` | __/ _ \/ __|
- | _ < __/ (_| | |_| |> < ___) | || (_| | || __/\__ \
- |_| \_\___|\__,_|\__,_/_/\_\ |____/ \__\__,_|\__\___||___/
-
- */
- import { useSelector, useDispatch } from "react-redux";
- import { updateCurrentTerminalCommand } from "../../states/global";
- import {
- AppState,
- updateSetToUploadFile,
- updateSetToReloadPath,
- updateIsDeviceUploadingFile,
- updatePrintData
- } from "../../states/global";
- /*
- _ _ ____
- / \ _ __ | |_| _ \
- / _ \ | '_ \| __| | | |
- / ___ \| | | | |_| |_| |
- /_/ \_\_| |_|\__|____/
-
- */
- import { Button } from "antd";
- import { notification } from "antd";
- import { upload } from "../../utils/fileManager";
- import openNotificationWithIcon from "../../utils/notifications";
- import FileManagerUpload from "../fileManager/Upload"
- import {
- AbortController,
- Consumable,
- WritableStream,
- } from "@yume-chan/stream-extra";
- /*
- ____ ____ _ ____ _ _ _
- | _ \ _ _ _ __ / ___|___ _ __ ___ _ __ ___ __ _ _ __ __| / ___|| |__ ___| | |
- | |_) | | | | '_ \| | / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` \___ \| '_ \ / _ \ | |
- | _ <| |_| | | | | |__| (_) | | | | | | | | | | | (_| | | | | (_| |___) | | | | __/ | |
- |_| \_\\__,_|_| |_|\____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|____/|_| |_|\___|_|_|
-
- */
- const RunCommandShell: React.FC = () => {
- const [api, contextHolder] = notification.useNotification();
- const [command, setCommand] = useState<any>("");
- const [runState, setRunState] = useState(false);
- const currentDevice = useSelector((state: AppState) => state.currentDevice);
- const pythonCode = useSelector((state: AppState) => state.blocklyCode);
- const printData = useSelector((state: AppState) => state.printData);
- const isBackendConnected = useSelector(
- (state: AppState) => state.isBackendConnected,
- );
- const isDeviceUploadingFile = useSelector(
- (state: AppState) => state.isDeviceUploadingFile,
- );
- const dispatch = useDispatch();
- useEffect(() => {
- }, [currentDevice]);
- const handleOnSearch = async () => {
- console.log("开始上传文件", pythonCode);
- try {
- await uploadFile()
- await uploadFileOut();
- await runCode();
- } catch (e: any) {
- console.log("上传错误:", e);
- openNotificationWithIcon(
- api,
- "error",
- "bottomRight",
- "Failed to upload file",
- e,
- );
- } finally {
- console.log("上传完成");
- // openNotificationWithIcon(
- // api,
- // "success",
- // "bottomRight",
- // "Successfully uploaded file",
- // "Uploaded to /mnt/UDISK/user_latest_code.py",
- // );
- }
- }
- const uploadFile = async () => {
- const deviceSync = currentDevice?.sync();
- if (!deviceSync) {
- return;
- }
- const strText = `import traceback
- import logging
- import sys
- import os
- import time
- logger = logging.getLogger()
- logger.setLevel(logging.INFO)
- file_handler = logging.FileHandler('/root/output.log')
- file_handler.setLevel(logging.INFO)
- formatter = logging.Formatter('%(message)s')
- file_handler.setFormatter(formatter)
- logger.addHandler(file_handler)
- def log_output(*args, **kwargs):
- output = ' '.join(map(str, args))
- logger.info(output.strip(), **kwargs)
- sys.stderr.write = sys.stdout.write = log_output
- os.close(1)
- os.close(2)
- try:
- source_code = r'''import time
- from maix import *
- import gc
- import os,sys
- import sys
- sys.path.append('/root/')
- from CocoPi import BUTTON
- from CocoPi import stm8s
- from CocoPi import multiFuncGpio
- from CocoPi import singleRgb
- from CocoPi import LED
- gc.enable()
- gc.collect()
- iic_slaver=stm8s()
- iic_slaver.clear()
- PIXEL_LED1= multiFuncGpio(0,0)
- PIXEL_LED2= multiFuncGpio(1,0)
- PIXEL_LED1.pixelInit_()
- PIXEL_LED2.pixelInit_()
- time.sleep(0.1)
- L1=singleRgb()
- L1.setColor(0,0,0)
- L1.show()
- time.sleep(0.2)
- L2=LED()
- L2.out(0)
- del iic_slaver
- del PIXEL_LED1
- del PIXEL_LED2
- del L1
- del L2
- ${pythonCode}
- '''
- exec(source_code)
- except Exception as e:
- error_info = traceback.format_exc()
- print(error_info)
- `;
- const file = new File([strText], "event", {
- type: "text/plain",
- });
- try {
- dispatch(updateIsDeviceUploadingFile(true));
- dispatch(updateSetToUploadFile(true));
- await upload(file, "/root/", await deviceSync);
- } catch (e: any) {
- openNotificationWithIcon(
- api,
- "error",
- "bottomRight",
- "Failed to upload file",
- e,
- );
- } finally {
- dispatch(updateSetToUploadFile(false));
- dispatch(updateSetToReloadPath(true));
- dispatch(updateIsDeviceUploadingFile(false));
- }
- }
- const uploadFileOut = async () => {
- const deviceSync = currentDevice?.sync();
- if (!deviceSync) {
- return;
- }
- let out = `import asyncio
- import subprocess
- import linecache
- async def tailf(filename):
- command = "python -u /root/event"
- process = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
- print("")
- while True:
- await asyncio.sleep(0.03)
- try:
- linecache.clearcache()
- logarr = linecache.getlines(filename)[-20:]
- if len(logarr) == 0:
- if process.poll() is None:
- continue
- else:
- return
- open(filename, 'w').close()
- print("".join(logarr).strip())
- except asyncio.TimeoutError:
- pass
- filename = "/root/output.log"
- loop = asyncio.get_event_loop()
- loop.run_until_complete(tailf(filename))
- loop.close()
- `
- const fileOut = new File([out], "out.py", {
- type: "text/plain",
- });
- try {
- dispatch(updateIsDeviceUploadingFile(true));
- dispatch(updateSetToUploadFile(true));
- await upload(fileOut, "/root/", await deviceSync)
- } catch (e: any) {
- openNotificationWithIcon(
- api,
- "error",
- "bottomRight",
- "Failed to upload file",
- e,
- );
- } finally {
- dispatch(updateSetToUploadFile(false));
- dispatch(updateSetToReloadPath(true));
- dispatch(updateIsDeviceUploadingFile(false));
- }
- }
- const runCode = async () => {
- if (command) {
- command.kill().then(() => {
- runCommand()
- })
- } else {
- runCommand()
- }
- }
- // 运行命令
- const runCommand = async () => {
- window.device?.subprocess.shell(`(echo '' > /root/output.log ; touch /tmp/disable) && python -u /root/out.py;`).then(e => {
- setCommand(e);
- serverPrint(e);
- }).catch(err => {
- console.log(err);
- })
- };
- const serverPrint = async (e: any) => {
- let _socketAbortController = new AbortController()
- setRunState(true);
- let data = printData;
- e._stdout.pipeTo(new WritableStream<Uint8Array>({
- write: (chunk) => {
- // Decode the chunk to obtain the output text
- const outputText = new TextDecoder().decode(chunk);
- data = data +`<p>${outputText}</p>`;
- dispatch(updatePrintData(data));
- // this.output.push(outputText);
- },
- }),
- {
- signal: _socketAbortController.signal,
- },
- ).catch((error: any) => {
- console.error("Error writing to writable stream:", error);
- }
- );
- }
- const stopOperation = () => {
- // dispatch(updateCurrentTerminalCommand(`rm /tmp/disable`));
- command.kill().then(() => {
- window.device?.subprocess.shell(`rm /tmp/disable`).then(e => {
- setCommand(e);
- setRunState(false);
- })
- })
- }
- return (
- <>
- {contextHolder}
- <Button type="primary" disabled={!isBackendConnected || isDeviceUploadingFile} onClick={() => handleOnSearch()}>运行</Button>
- <FileManagerUpload />
- <Button type="primary" disabled={!runState} onClick={() => stopOperation()}>停止</Button>
- </>
- );
- };
- export default RunCommandShell;
|