--- # ๐Ÿ“˜ๅŠŸ่ƒฝ่ฏดๆ˜Žๆ–‡ๆกฃ๏ผˆFSD๏ผ‰ ## ๆจกๅ—ๅ็งฐ๏ผšPython ๅœจ็บฟ่„šๆœฌ็ผ–่พ‘ๅ™จ๏ผˆPython Script Editor๏ผ‰ --- ### ไธ€ใ€ๆจกๅ—ๅฎšไฝ ไธบๅนณๅฐๆไพ› **ๅฏๆ‰ฉๅฑ•็š„ Python ่„šๆœฌๆ‰ง่กŒๅผ•ๆ“Ž** ็š„่ƒฝๅŠ›๏ผŒๆ”ฏๆŒ๏ผš - **ๆ–‡ไปถๅˆ›ๅปบไธŽ็ฎก็†**๏ผšๆ”ฏๆŒ็›ฎๅฝ•ๅ’Œ่„šๆœฌๆ–‡ไปถ็š„ๅˆ›ๅปบใ€็ผ–่พ‘ใ€ๅˆ ้™คๆ“ไฝœ - **่„šๆœฌ่ฟ่กŒๆŽงๅˆถ**๏ผšๆไพ›่„šๆœฌๅฏๅŠจใ€ๅœๆญขใ€้‡ๅฏ็ญ‰็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็† - **ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€**๏ผš้€š่ฟ‡ WebSocket ๅฎžๆ—ถๆŽจ้€่„šๆœฌๆ‰ง่กŒๆ—ฅๅฟ—ๅ’Œ่พ“ๅ‡บ็ป“ๆžœ - **ๆ—ฅๅฟ—่ฎฐๅฝ•ๅญ˜ๅ‚จ**๏ผšๅฐ†่„šๆœฌๆ‰ง่กŒ่ฟ‡็จ‹ใ€็ป“ๆžœใ€ๅผ‚ๅธธ็ญ‰ไฟกๆฏๆŒไน…ๅŒ–ๅญ˜ๅ‚จ - **ๆŽฅๅฃ้›†ๆˆๅŠŸ่ƒฝ**๏ผšไธŽ VWED ไปปๅŠก็ณป็ปŸๆทฑๅบฆ้›†ๆˆ๏ผŒๆ”ฏๆŒไปปๅŠกๆต็จ‹ไธญ่ฐƒ็”จ่„šๆœฌ - **่‡ชๅฎšไน‰ๆจกๅ—ๆ‰ฉๅฑ•**๏ผšๆ”ฏๆŒๆณจๅ†Œ่‡ชๅฎšไน‰ๆŽฅๅฃใ€ๅ‡ฝๆ•ฐใ€ไบ‹ไปถ็›‘ๅฌๅ™จ็ญ‰ - **ๅ‡ฝๆ•ฐๆณจๅ†Œ่ฐƒ็”จ**๏ผšๆ”ฏๆŒๅœจ VWED ไปปๅŠกไธญ้€š่ฟ‡่„šๆœฌๆจกๅ—ๅผ•็”จๅ’Œ่ฟ่กŒๆณจๅ†Œ็š„ๅ‡ฝๆ•ฐ - **ๅคš่„šๆœฌๅนถๅ‘ๆœๅŠก**๏ผšๆฏไธช่„šๆœฌ็‹ฌ็ซ‹่ฟ่กŒ๏ผŒๆ”ฏๆŒๅคš่„šๆœฌๅŒๆ—ถๆไพ›ๆœๅŠก - **ไผไธš็บงๅฎ‰ๅ…จๆฒ™็ฎฑ**๏ผšๆไพ›ๅฎ‰ๅ…จ็š„่„šๆœฌๆ‰ง่กŒ็Žฏๅขƒๅ’Œๆƒ้™ๆŽงๅˆถ --- ### ไบŒใ€ๅŸบไบŽ็Žฐๆœ‰ๆžถๆž„็š„่„šๆœฌๅผ•ๆ“Žๆก†ๆžถ ``` VWED่„šๆœฌๅผ•ๆ“Žๆžถๆž„๏ผˆๅŸบไบŽ็Žฐๆœ‰้กน็›ฎ็ป“ๆž„๏ผ‰ โ”œโ”€โ”€ ๐ŸŒ APIๆŽฅๅฃๅฑ‚ (routes/) โ”‚ โ”œโ”€โ”€ script_engine_api.py # REST APIๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ script_websocket_api.py # WebSocket APIๆŽฅๅฃ โ”‚ โ””โ”€โ”€ model/script_engine_model.py # ่ฏทๆฑ‚/ๅ“ๅบ”ๆจกๅž‹ โ”œโ”€โ”€ ๐Ÿ”ง ไธšๅŠกๆœๅŠกๅฑ‚ (services/) โ”‚ โ”œโ”€โ”€ script_engine_service.py # ๆ ธๅฟƒๅผ•ๆ“ŽๆœๅŠก โ”‚ โ”œโ”€โ”€ script_registry_service.py # ๆณจๅ†ŒไธญๅฟƒๆœๅŠก โ”‚ โ”œโ”€โ”€ script_builtin_functions.py # ๅ†…็ฝฎๅ‡ฝๆ•ฐๆœๅŠก โ”‚ โ”œโ”€โ”€ script_security_sandbox.py # ๅฎ‰ๅ…จๆฒ™็ฎฑๆœๅŠก โ”‚ โ””โ”€โ”€ script_websocket_logger.py # WebSocketๆ—ฅๅฟ—ๆœๅŠก โ”œโ”€โ”€ ๐Ÿ”— ไปปๅŠก้›†ๆˆๅฑ‚ (services/execution/handlers/) โ”‚ โ””โ”€โ”€ script_engine_integration.py # ไปปๅŠกๆต็จ‹้›†ๆˆ โ”œโ”€โ”€ ๐Ÿ’พ ๆ•ฐๆฎๆจกๅž‹ๅฑ‚ (data/models/) โ”‚ โ”œโ”€โ”€ script_project.py # ่„šๆœฌ้กน็›ฎๆจกๅž‹ โ”‚ โ”œโ”€โ”€ script_file.py # ่„šๆœฌๆ–‡ไปถๆจกๅž‹ โ”‚ โ”œโ”€โ”€ script_api_registration.py # APIๆณจๅ†Œ่ฎฐๅฝ•ๆจกๅž‹ โ”‚ โ”œโ”€โ”€ script_function_registration.py # ๅ‡ฝๆ•ฐๆณจๅ†Œ่ฎฐๅฝ•ๆจกๅž‹ โ”‚ โ”œโ”€โ”€ script_execution_log.py # ๆ‰ง่กŒๆ—ฅๅฟ—ๆจกๅž‹ โ”‚ โ””โ”€โ”€ script_registry.py # ่„šๆœฌๆณจๅ†Œๆจกๅž‹ โ”œโ”€โ”€ โš™๏ธ ้…็ฝฎๅฑ‚ (config/) โ”‚ โ””โ”€โ”€ components/script.json # ่„šๆœฌ็ป„ไปถ้…็ฝฎ โ””โ”€โ”€ ๐Ÿ“Š ไปปๅŠกๆต็จ‹้›†ๆˆ โ”œโ”€โ”€ SCRIPT_ENGINE_EXECUTE๏ผˆๆ‰ง่กŒ่„šๆœฌ๏ผ‰ โ”œโ”€โ”€ SCRIPT_ENGINE_CALL_FUNCTION๏ผˆ่ฐƒ็”จๆณจๅ†Œๅ‡ฝๆ•ฐ๏ผ‰ โ””โ”€โ”€ SCRIPT_ENGINE_HTTP_REQUEST๏ผˆHTTP่ฏทๆฑ‚็ป„ไปถ๏ผ‰ ``` --- ### ไธ‰ใ€่„šๆœฌๅผ•ๆ“Žๆ ธๅฟƒ็‰นๆ€ง | ็‰นๆ€งๅ็งฐ | ๆ่ฟฐ | ๆŠ€ๆœฏๅฎž็Žฐ | |----------|------|----------| | ๆ–‡ไปถ็ฎก็†็ณป็ปŸ | ๆ”ฏๆŒ่„šๆœฌๆ–‡ไปถๅ’Œ็›ฎๅฝ•็š„CRUDๆ“ไฝœ | ๅŸบไบŽๆ–‡ไปถ็ณป็ปŸ็š„็›ฎๅฝ•็ป“ๆž„็ฎก็†๏ผŒๆ”ฏๆŒๅคšๅฑ‚็บง็›ฎๅฝ• | | ่„šๆœฌ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็† | ่„šๆœฌ็š„ๅฏๅŠจใ€ๅœๆญขใ€้‡ๅฏ็ญ‰็Šถๆ€ๆŽงๅˆถ | ScriptEngineๆœๅŠก็ฎก็†่„šๆœฌ่ฟ›็จ‹็”Ÿๅ‘ฝๅ‘จๆœŸ | | ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€ | WebSocketๅฎžๆ—ถๆŽจ้€่„šๆœฌๆ‰ง่กŒๆ—ฅๅฟ—ๅ’Œ็ป“ๆžœ | ScriptWebSocketLoggerๆœๅŠกๅฎž็Žฐๅฎžๆ—ถ้€šไฟก | | ๆ—ฅๅฟ—ๆŒไน…ๅŒ–ๅญ˜ๅ‚จ | ่„šๆœฌๆ‰ง่กŒ่ฟ‡็จ‹ๅ’Œ็ป“ๆžœ็š„ๆ•ฐๆฎๅบ“ๅญ˜ๅ‚จ | ๆ‰ง่กŒๆ—ฅๅฟ—ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“๏ผŒๆ”ฏๆŒๅކๅฒๆŸฅ่ฏข | | ไปปๅŠก็ณป็ปŸ้›†ๆˆ | ไธŽVWEDไปปๅŠกๆต็จ‹ๆทฑๅบฆ้›†ๆˆ่ฐƒ็”จ | script.pyๅค„็†ๅ™จๆ”ฏๆŒไปปๅŠกไธญ่ฐƒ็”จ่„šๆœฌๅ‡ฝๆ•ฐ | | ๅ‡ฝๆ•ฐๅŠจๆ€ๅŠ ่ฝฝ | ๅŠจๆ€ๅŠ ่ฝฝๅนถๆ‰ง่กŒ่„šๆœฌไธญ็š„ๆŒ‡ๅฎšๅ‡ฝๆ•ฐ | importlib.utilๅฎž็Žฐๆจกๅ—ๅŠจๆ€ๅŠ ่ฝฝๅ’Œๅ‡ฝๆ•ฐ่ฐƒ็”จ | | ๅ‚ๆ•ฐไผ ้€’ๆœบๅˆถ | ๆ”ฏๆŒๅคš็ง็ฑปๅž‹ๅ‚ๆ•ฐไผ ้€’็ป™่„šๆœฌๅ‡ฝๆ•ฐ | ๆ”ฏๆŒdictใ€listใ€str็ญ‰็ฑปๅž‹ๅ‚ๆ•ฐๆ™บ่ƒฝ่งฃๆž | | ๅผ‚ๆญฅๆ‰ง่กŒๆ”ฏๆŒ | ๆ”ฏๆŒๅผ‚ๆญฅๅ‡ฝๆ•ฐ็š„ๆ‰ง่กŒๅ’Œ็ป“ๆžœ็ญ‰ๅพ… | inspectๆฃ€ๆต‹ๅ็จ‹ๅนถไฝฟ็”จasyncio.awaitๅค„็† | | ๅ˜้‡ไธŠไธ‹ๆ–‡็ฎก็† | ่„šๆœฌๆ‰ง่กŒ็ป“ๆžœไฟๅญ˜ๅˆฐไปปๅŠกไธŠไธ‹ๆ–‡ | TaskContext็ฎก็†ไปปๅŠกๅ˜้‡๏ผŒๆ”ฏๆŒ่ทจ็ป„ไปถไผ ้€’ | | ้”™่ฏฏๅค„็†ๆœบๅˆถ | ๅฎŒๅ–„็š„ๅผ‚ๅธธๆ•่Žทๅ’Œ้”™่ฏฏไฟกๆฏๅ้ฆˆ | ๅผ‚ๅธธไฟกๆฏ่ฎฐๅฝ•ๅˆฐๆ—ฅๅฟ—๏ผŒ่ฟ”ๅ›ž่ฏฆ็ป†้”™่ฏฏๆ่ฟฐ | --- # ๐Ÿ“—้œ€ๆฑ‚ๆ–‡ๆกฃ๏ผˆPRD๏ผ‰ ## ไธ€ใ€็›ฎๆ ‡็”จๆˆท - ๅฎžๆ–ฝๅทฅ็จ‹ๅธˆ - ่‡ชๅŠจๅŒ–ๅทฅ็จ‹ๅธˆ - ไผš Python ็š„ไธšๅŠกๅผ€ๅ‘ไบบๅ‘˜ --- ## ไบŒใ€ๅŸบไบŽ็Žฐๆœ‰ๆžถๆž„็š„ๅŠŸ่ƒฝ้œ€ๆฑ‚ | ็ผ–ๅท | ้œ€ๆฑ‚ๅ็งฐ | ๆ่ฟฐ | ๅฝ“ๅ‰ๅฎž็Žฐ็Šถๆ€ | |------|----------|------|-------------| | BR01 | ่„šๆœฌๆ–‡ไปถๅˆ›ๅปบ็ฎก็† | ๆ”ฏๆŒ่„šๆœฌๆ–‡ไปถๅ’Œ็›ฎๅฝ•็š„ๅˆ›ๅปบใ€็ผ–่พ‘ใ€ๅˆ ้™ค | โœ… ๅทฒๅฎž็Žฐ - ๅŸบไบŽๆ–‡ไปถ็ณป็ปŸ็š„CRUDๆ“ไฝœ | | BR02 | ่„šๆœฌๆ‰ง่กŒๆŽงๅˆถ | ่„šๆœฌ็š„ๅฏๅŠจใ€ๅœๆญขใ€็Šถๆ€็›‘ๆŽง | โœ… ๅทฒๅฎž็Žฐ - ScriptEngine็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็† | | BR03 | ๅ‡ฝๆ•ฐๅŠจๆ€่ฐƒ็”จ | ๅŠจๆ€ๅŠ ่ฝฝ่„šๆœฌๆ–‡ไปถๅนถๆ‰ง่กŒๆŒ‡ๅฎšๅ‡ฝๆ•ฐ | โœ… ๅทฒๅฎž็Žฐ - importlib.utilๅŠจๆ€ๅŠ ่ฝฝๆœบๅˆถ | | BR04 | ๅ‚ๆ•ฐไผ ้€’ๆ”ฏๆŒ | ๆ”ฏๆŒๅคš็งๆ•ฐๆฎ็ฑปๅž‹็š„ๅ‚ๆ•ฐไผ ้€’ | โœ… ๅทฒๅฎž็Žฐ - dict/list/strๅ‚ๆ•ฐๆ™บ่ƒฝ่งฃๆž | | BR05 | ๅผ‚ๆญฅๆ‰ง่กŒๆ”ฏๆŒ | ๆ”ฏๆŒๅผ‚ๆญฅๅ‡ฝๆ•ฐ็š„ๆ‰ง่กŒๅ’Œ็ป“ๆžœ็ญ‰ๅพ… | โœ… ๅทฒๅฎž็Žฐ - inspect+asyncioๅผ‚ๆญฅๅค„็† | | BR06 | ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€ | WebSocketๅฎžๆ—ถๆŽจ้€ๆ‰ง่กŒๆ—ฅๅฟ—ๅ’Œ็ป“ๆžœ | โœ… ๅทฒๅฎž็Žฐ - WebSocketๅฎžๆ—ถ้€šไฟก | | BR07 | ๆ—ฅๅฟ—ๆŒไน…ๅŒ– | ๆ‰ง่กŒ่ฟ‡็จ‹ๅ’Œ็ป“ๆžœ็š„ๆ•ฐๆฎๅบ“ๅญ˜ๅ‚จ | โœ… ๅทฒๅฎž็Žฐ - ๆ‰ง่กŒๆ—ฅๅฟ—ๆ•ฐๆฎๅบ“่ฎฐๅฝ• | | BR08 | ไปปๅŠก็ณป็ปŸ้›†ๆˆ | ๅœจVWEDไปปๅŠกๆต็จ‹ไธญ่ฐƒ็”จ่„šๆœฌๅ‡ฝๆ•ฐ | โœ… ๅทฒๅฎž็Žฐ - script.pyๅค„็†ๅ™จ้›†ๆˆ | | BR09 | ๅ˜้‡ไธŠไธ‹ๆ–‡็ฎก็† | ่„šๆœฌๆ‰ง่กŒ็ป“ๆžœไฟๅญ˜ๅˆฐไปปๅŠกๅ˜้‡ | โœ… ๅทฒๅฎž็Žฐ - TaskContextๅ˜้‡็ฎก็† | | BR10 | ้”™่ฏฏๅค„็†ๆœบๅˆถ | ๅฎŒๅ–„็š„ๅผ‚ๅธธๆ•่Žทๅ’Œ้”™่ฏฏไฟกๆฏๅ้ฆˆ | โœ… ๅทฒๅฎž็Žฐ - ๅคšๅฑ‚ๅผ‚ๅธธๅค„็†ๅ’Œๆ—ฅๅฟ—่ฎฐๅฝ• | --- ## ไธ‰ใ€้žๅŠŸ่ƒฝ้œ€ๆฑ‚๏ผˆไธๅ˜๏ผ‰ | ็ฑปๅž‹ | ่ฆๆฑ‚ | |------|------| | ๆ€ง่ƒฝ | ่„šๆœฌๅŠ ่ฝฝ < 1s๏ผŒๆŽฅๅฃๅ“ๅบ” < 200ms | | ๅฎ‰ๅ…จ | ่„šๆœฌๆฒ™็ฎฑ่ฟ่กŒ๏ผŒ็ฆๆญข่ฎฟ้—ฎ็ณป็ปŸๆ–‡ไปถใ€็ฝ‘็ปœ๏ผˆ้™ค็™ฝๅๅ•๏ผ‰ | | ๅฏ็ปดๆŠคๆ€ง | ่„šๆœฌ็ปŸไธ€ๅญ˜ๅ‚จ๏ผŒๆ”ฏๆŒ็‰ˆๆœฌๆŽงๅˆถ๏ผˆGit๏ผ‰ | | ๅฏๆ‰ฉๅฑ•ๆ€ง | ๅŽ็ปญๅฏๆ”ฏๆŒ Python3 ่™šๆ‹Ÿ็Žฏๅขƒใ€pip ๅฎ‰่ฃ…ไพ่ต– | | ๅ…ผๅฎนๆ€ง | ๆ”ฏๆŒ Chromeใ€Edgeใ€Firefox | --- # ๐Ÿ“™ๅผ€ๅ‘ๆ–‡ๆกฃ๏ผˆTDD๏ผ‰ ## ไธ€ใ€ๅŸบไบŽ็Žฐๆœ‰ๆžถๆž„็š„ๆŠ€ๆœฏ้€‰ๅž‹ | ๆžถๆž„ๅฑ‚็บง | ๆŠ€ๆœฏ้€‰ๅž‹ | ๅ…ทไฝ“ๅฎž็Žฐ | |----------|----------|----------| | APIๆŽฅๅฃๅฑ‚ | FastAPI + WebSocket | routes/script_engine_api.py + routes/script_websocket_api.py | | ไธšๅŠกๆœๅŠกๅฑ‚ | Service Pattern + ไพ่ต–ๆณจๅ…ฅ | services/script_engine_service.py ็ญ‰ๆœๅŠกๆจกๅ— | | ๆ•ฐๆฎๆจกๅž‹ๅฑ‚ | SQLAlchemy ORM + Enum | data/models/script_*.py + data/enum/ | | ไปปๅŠก้›†ๆˆๅฑ‚ | VWEDไปปๅŠกๆ‰ง่กŒ็ณป็ปŸ | services/execution/handlers/script_engine_integration.py | | ๅฎ‰ๅ…จ้˜ฒๆŠคๅฑ‚ | Pythonๆฒ™็ฎฑ + ๆƒ้™ๆŽงๅˆถ | services/script_security_sandbox.py | | ้…็ฝฎ็ฎก็†ๅฑ‚ | JSON้…็ฝฎ + ็ป„ไปถ็ณป็ปŸ | config/components/script.json | | ๆ—ฅๅฟ—็›‘ๆŽงๅฑ‚ | WebSocketๅฎžๆ—ถๆŽจ้€ + ็ป“ๆž„ๅŒ–ๆ—ฅๅฟ— | services/script_websocket_logger.py | | ๆ•ฐๆฎๅบ“ๅฑ‚ | MySQL/PostgreSQL + ่ฟžๆŽฅๆฑ  | data/session.py + migrations/ | --- ## ไบŒใ€ๅŸบไบŽ็Žฐๆœ‰ๆžถๆž„็š„่„šๆœฌๅผ•ๆ“Ž่ฎพ่ฎก ### 2.1 ๅฏๆ‰ฉๅฑ•ๆก†ๆžถๆ•ดไฝ“ๆžถๆž„ ```text โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅ‰็ซฏ็ผ–่พ‘็•Œ้ข (Monaco Editor) โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ่„šๆœฌ็ผ–่พ‘ๅ™จ โ”‚ ๅฎžๆ—ถๆ—ฅๅฟ— โ”‚ ่ฐƒ่ฏ•ๆŽงๅฐ โ”‚ ๆ€ง่ƒฝ็›‘ๆŽง โ”‚ โ”‚ โ”‚ โ”‚- Python่ฏญๆณ•้ซ˜ไบฎโ”‚- WebSocketๆ—ฅๅฟ—โ”‚- remote-pdb โ”‚- CPU/ๅ†…ๅญ˜ โ”‚ โ”‚ โ”‚ โ”‚- ไปฃ็ ๆ™บ่ƒฝ่กฅๅ…จ โ”‚- ็ญ‰็บง่ฟ‡ๆปค โ”‚- ๆ–ญ็‚น่ฐƒ่ฏ• โ”‚- ่„šๆœฌๆ‰ง่กŒ็Šถๆ€ โ”‚ โ”‚ โ”‚ โ”‚- ่ฏญๆณ•ๆฃ€ๆŸฅ&ๆ็คบ โ”‚- ๆ—ฅๅฟ—ๅฏผๅ‡บ โ”‚- ๅ˜้‡็›‘ๆŽง โ”‚- ่ต„ๆบไฝฟ็”จ็ปŸ่ฎก โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ HTTP/WebSocket API โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ”Œ ๆŽฅๅฃๆœๅŠกๅฑ‚ (scripts/interfaces/) - ๅฏๆ‰ฉๅฑ•ๅ่ฎฎๆ”ฏๆŒ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ScriptEngineAPI โ”‚ScriptWebSocketAPIโ”‚ScriptManagementAPIโ”‚ ๅ่ฎฎๆ‰ฉๅฑ• โ”‚ โ”‚ โ”‚ โ”‚- ่„šๆœฌๆ‰ง่กŒๆŽงๅˆถ โ”‚- ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€ โ”‚- ๆ–‡ไปถ็ฎก็† โ”‚- gRPC/MQTT โ”‚ โ”‚ โ”‚ โ”‚- ็Šถๆ€็›‘ๆŽง โ”‚- ่ฐƒ่ฏ•้€šไฟก โ”‚- ๆƒ้™็ฎก็† โ”‚- TCP/UDP โ”‚ โ”‚ โ”‚ โ”‚- APIๆณจๅ†Œ็ฎก็† โ”‚- ๆ€ง่ƒฝๆŒ‡ๆ ‡ โ”‚- ็‰ˆๆœฌๆŽงๅˆถ โ”‚- ่‡ชๅฎšไน‰ๅ่ฎฎ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ ๆœๅŠก่ฐƒ็”จ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ”ง ไธšๅŠกๆœๅŠกๅฑ‚ (scripts/services/) - ๆœๅŠกๆณจๅ†ŒไธŽๅ‘็Žฐ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ScriptEngine โ”‚ Registry โ”‚BuiltinFunctionsโ”‚WebSocketLoggerโ”‚ โ”‚ โ”‚ โ”‚- ๅคš่„šๆœฌๅนถๅ‘ โ”‚- ่ต„ๆบๆณจๅ†Œ โ”‚- VWEDๅฏน่ฑกๆจกๅ—โ”‚- ๆ—ฅๅฟ—็›‘ๆŽง โ”‚ โ”‚ โ”‚ โ”‚- ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†โ”‚- script_id้š”็ฆปโ”‚- ๆ‰ฉๅฑ•ๆจกๅ—ๅŠ ่ฝฝโ”‚- ๅฎžๆ—ถๆŽจ้€ โ”‚ โ”‚ โ”‚ โ”‚- ่ต„ๆบๆธ…็† โ”‚- ๅŠจๆ€ๆณจ้”€ โ”‚- ็ƒญๆ›ดๆ–ฐๆ”ฏๆŒ โ”‚- ๆ—ฅๅฟ—่šๅˆ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ ๆจกๅ—ๅŠ ่ฝฝ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿงฉ ๆจกๅ—ๆ‰ฉๅฑ•ๅฑ‚ (scripts/modules/) - ๆฏไธชๆจกๅ—็‹ฌ็ซ‹ๅฏๆ‰ฉๅฑ• โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๅ†…็ฝฎๆจกๅ—็”Ÿๆ€ (VWEDๅฏน่ฑก็ณป็ปŸ) โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ Coreๆจกๅ— โ”‚ Taskๆจกๅ— โ”‚Deviceๆจกๅ— โ”‚ Commๆจกๅ— โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚- api โ”‚- task โ”‚- device โ”‚- http โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚- function โ”‚- timer โ”‚- robot โ”‚- websocket โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚- event โ”‚- scheduler โ”‚- modbus โ”‚- tcp โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ”‚ โ”‚Storageๆจกๅ—โ”‚ Logๆจกๅ— โ”‚Utilityๆจกๅ—โ”‚ ่‡ชๅฎšไน‰ๆจกๅ— โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚- storage โ”‚- log โ”‚- mail โ”‚- AIๆจกๅ— โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚- database โ”‚- alert โ”‚- crypto โ”‚- ่ง†่ง‰ๆจกๅ— โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚- cache โ”‚- audit โ”‚- file โ”‚- ็ฎ—ๆณ•ๆจกๅ— โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โš™๏ธ ๆ ธๅฟƒๅผ•ๆ“Žๅฑ‚ (scripts/engine/) - ๅคš่„šๆœฌๅนถๅ‘็ฎก็† โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ VWEDGlobalRegistry (ๅ…จๅฑ€ๆณจๅ†Œไธญๅฟƒ) โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ”‚ โ”‚MultiScript โ”‚SecuritySandboxโ”‚LifecycleManagerโ”‚ ResourceTrackerโ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚Engine โ”‚ ๅฎ‰ๅ…จๆฒ™็ฎฑ โ”‚ ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็† โ”‚ ่ต„ๆบ่ทŸ่ธช โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚-ๅคš่„šๆœฌๅนถๅ‘ โ”‚- ๆจกๅ—็™ฝๅๅ• โ”‚- boot()ๅ…ฅๅฃๅ‡ฝๆ•ฐโ”‚- script_id้š”็ฆปโ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚-script_id้š”็ฆปโ”‚- ่ต„ๆบ้™ๅˆถ โ”‚- ๅผ‚ๅธธๅค„็† โ”‚- ่ต„ๆบ่‡ชๅŠจๅ›žๆ”ถโ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚-็Šถๆ€็ฎก็† โ”‚- ๆƒ้™ๆŽงๅˆถ โ”‚- ้‡ๅฏๆœบๅˆถ โ”‚- ๆ€ง่ƒฝ็›‘ๆŽง โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๐Ÿ’พ ๆ•ฐๆฎๆจกๅž‹ๅฑ‚ (scripts/models/) - ๆŒไน…ๅŒ–ไธŽๅญ˜ๅ‚จ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ScriptProject โ”‚ ScriptFile โ”‚ExecutionLog โ”‚APIRegistration โ”‚ โ”‚ โ”‚ โ”‚- ้กน็›ฎ็ฎก็† โ”‚- ๆ–‡ไปถๅญ˜ๅ‚จ โ”‚- ๆ‰ง่กŒ่ฎฐๅฝ• โ”‚- APIๆณจๅ†Œ่ฎฐๅฝ• โ”‚ โ”‚ โ”‚ โ”‚- ็›ฎๅฝ•็ป“ๆž„ โ”‚- ็‰ˆๆœฌ็ฎก็† โ”‚- ้”™่ฏฏๆ—ฅๅฟ— โ”‚- ๅ‡ฝๆ•ฐๆณจๅ†Œ่ฎฐๅฝ•โ”‚ โ”‚ โ”‚ โ”‚- ๆƒ้™ๆŽงๅˆถ โ”‚- ๅ†…ๅฎนๆฃ€็ดข โ”‚- ๆ€ง่ƒฝๆŒ‡ๆ ‡ โ”‚- ่ทฏ็”ฑๆณจๅ†Œ่ฎฐๅฝ•โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### 2.2 ๆณจๅ†Œไธญๅฟƒ่ฏฆ็ป†ๆœบๅˆถ #### 2.2.1 APIๆŽฅๅฃๆณจๅ†ŒไธŽ่ฎฟ้—ฎๆต็จ‹ ```text ใ€ๅœจ็บฟ่„šๆœฌๆณจๅ†Œ้˜ถๆฎตใ€‘ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅœจ็บฟ่„šๆœฌ: VWED.api.register_route() โ”‚ โ”‚ def boot(): โ”‚ โ”‚ VWED.api.register_route( โ”‚ โ”‚ path="/script-api/custom", โ”‚ โ”‚ method="POST", โ”‚ โ”‚ handler=my_custom_handler โ”‚ โ”‚ ) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅ…จๅฑ€ๆณจๅ†Œไธญๅฟƒๅญ˜ๅ‚จ โ”‚ โ”‚ api_routes = { โ”‚ โ”‚ "/script-api/custom": { โ”‚ โ”‚ "method": "POST", โ”‚ โ”‚ "handler": my_custom_handler, โ”‚ โ”‚ "script_id": "script_001", โ”‚ โ”‚ "registered_at": "2024-01-01 12:00:00" โ”‚ โ”‚ } โ”‚ โ”‚ } โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ FastAPI ๅŠจๆ€่ทฏ็”ฑๆณจๅ†Œ โ”‚ โ”‚ @app.route("/script-api/{path:path}", methods=ALL) โ”‚ โ”‚ async def dynamic_script_route(path, request): โ”‚ โ”‚ # ๆŸฅๆ‰พๆณจๅ†Œ่กจ โ”‚ โ”‚ route_info = registry.get_api_route(path) โ”‚ โ”‚ if route_info: โ”‚ โ”‚ return await route_info["handler"](request) โ”‚ โ”‚ else: โ”‚ โ”‚ return {"error": "ๆŽฅๅฃๆœชๆณจๅ†Œ"} โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ใ€HTTP่ฏทๆฑ‚ๅ—่ฎฟ้—ฎ้˜ถๆฎตใ€‘ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ไปปๅŠกๆต็จ‹ไธญ็š„HTTP่ฏทๆฑ‚ๅ— (htttp_request.py) โ”‚ โ”‚ async def execute(): โ”‚ โ”‚ url = "http://localhost:8000/script-api/custom" โ”‚ โ”‚ response = await http_client.post(url, data={}) โ”‚ โ”‚ # ่ฟ™ไผš่งฆๅ‘ไธŠ้ข็š„ๅŠจๆ€่ทฏ็”ฑ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๆ‰ง่กŒ็”จๆˆทๆณจๅ†Œ็š„ๅ‡ฝๆ•ฐ โ”‚ โ”‚ def my_custom_handler(request): โ”‚ โ”‚ # ็”จๆˆท่‡ชๅฎšไน‰็š„ไธšๅŠก้€ป่พ‘ โ”‚ โ”‚ data = request.json() โ”‚ โ”‚ result = process_business_logic(data) โ”‚ โ”‚ return {"success": True, "data": result} โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ใ€่„šๆœฌๆ‰ง่กŒๅ—่ฐƒ็”จ้˜ถๆฎตใ€‘ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ไปปๅŠกๆต็จ‹ไธญ็š„่„šๆœฌๆ‰ง่กŒๅ— (script.py) โ”‚ โ”‚ async def _execute_script(): โ”‚ โ”‚ # ้™คไบ†ๅŠ ่ฝฝๆ–‡ไปถๅ‡ฝๆ•ฐๅค–๏ผŒ่ฟ˜ๅฏไปฅ่ฐƒ็”จๆณจๅ†Œ็š„ๅ‡ฝๆ•ฐ โ”‚ โ”‚ registered_func = registry.get_registered_function(โ”‚ โ”‚ function_name โ”‚ โ”‚ ) โ”‚ โ”‚ if registered_func: โ”‚ โ”‚ return await registered_func(function_args) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` #### 2.2.2 ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐๆณจๅ†Œๆœบๅˆถ ```text ใ€ๅ‡ฝๆ•ฐๆณจๅ†Œใ€‘ VWED.function.register(name="calculate_distance", handler=calc_func) โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅ‡ฝๆ•ฐๆณจๅ†Œ่กจ โ”‚ โ”‚ registered_functions = { โ”‚ โ”‚ "calculate_distance": { โ”‚ โ”‚ "handler": calc_func, โ”‚ โ”‚ "script_id": "script_001", โ”‚ โ”‚ "description": "่ฎก็ฎ—ไธค็‚น่ท็ฆป", โ”‚ โ”‚ "params": ["point1", "point2"] โ”‚ โ”‚ } โ”‚ โ”‚ } โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ ใ€ๅ‡ฝๆ•ฐ่ฐƒ็”จใ€‘ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ script.py ๆ‰ง่กŒๅ— โ”‚ โ”‚ function_name = "calculate_distance" โ”‚ โ”‚ registered_func = registry.get_function(function_name)โ”‚ โ”‚ result = await registered_func(function_args) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` #### 2.2.3 ไบ‹ไปถ็ณป็ปŸ้›†ๆˆ ```text ใ€ไบ‹ไปถๆณจๅ†Œใ€‘ VWED.event.listen(event_name="task_completed", handler=on_complete) โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ไบ‹ไปถ็›‘ๅฌๅ™จๆณจๅ†Œ่กจ โ”‚ โ”‚ event_listeners = { โ”‚ โ”‚ "task_completed": [ โ”‚ โ”‚ { โ”‚ โ”‚ "handler": on_complete, โ”‚ โ”‚ "script_id": "script_001", โ”‚ โ”‚ "priority": 1 โ”‚ โ”‚ } โ”‚ โ”‚ ] โ”‚ โ”‚ } โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ ใ€ไบ‹ไปถ่งฆๅ‘ใ€‘ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ็ณป็ปŸๅ…ถไป–ๆจกๅ—่งฆๅ‘ไบ‹ไปถ โ”‚ โ”‚ from services.script_engine import script_registry โ”‚ โ”‚ await script_registry.emit_event( โ”‚ โ”‚ "task_completed", โ”‚ โ”‚ {"task_id": 123, "status": "success"} โ”‚ โ”‚ ) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ไธ‰ใ€่„šๆœฌๆœๅŠก็”Ÿๅ‘ฝๅ‘จๆœŸไธŽๅŠŸ่ƒฝๅฎž็Žฐ ### 3.1 ๆ–‡ไปถๅˆ›ๅปบไธŽ็ฎก็†ๅŠŸ่ƒฝ 1. **็›ฎๅฝ•ๅˆ›ๅปบ**๏ผš - ๆ”ฏๆŒๅœจ `scripts/user_save/` ็›ฎๅฝ•ไธ‹ๅˆ›ๅปบๅคšๅฑ‚็บง็›ฎๅฝ•็ป“ๆž„ - ๆไพ›็›ฎๅฝ•้‡ๅ‘ฝๅใ€ๅˆ ้™ค็ญ‰็ฎก็†ๅŠŸ่ƒฝ 2. **่„šๆœฌๆ–‡ไปถๅˆ›ๅปบ**๏ผš - ๅœจๆŒ‡ๅฎš็›ฎๅฝ•ไธ‹ๅˆ›ๅปบ `.py` ่„šๆœฌๆ–‡ไปถ - ๆไพ›ๆ–‡ไปถ้‡ๅ‘ฝๅใ€ๅˆ ้™คใ€ๅคๅˆถ็ญ‰ๆ“ไฝœ - ๆ”ฏๆŒๆ–‡ไปถๅ†…ๅฎน็š„ๅœจ็บฟ็ผ–่พ‘ๅ’Œไฟๅญ˜ 3. **่„šๆœฌๆจกๆฟ็”Ÿๆˆ**๏ผš - ๆ–ฐๅปบ่„šๆœฌๆ—ถ่‡ชๅŠจ็”Ÿๆˆๆ ‡ๅ‡†ๆจกๆฟไปฃ็  - ๅŒ…ๅซๅธธ็”จ็š„ๅ‡ฝๆ•ฐๅฎšไน‰ๅ’Œๆณจ้‡Š็ป“ๆž„ ### 3.2 ่„šๆœฌๆ‰ง่กŒๆŽงๅˆถๅŠŸ่ƒฝ 1. **่„šๆœฌๅฏๅŠจ่ฟ่กŒ**๏ผš - ้€š่ฟ‡ API ๆŽฅๅฃๅฏๅŠจๆŒ‡ๅฎš่„šๆœฌๆ–‡ไปถๆ‰ง่กŒ - ๆ”ฏๆŒไผ ้€’ๅฏๅŠจๅ‚ๆ•ฐๅ’Œ้…็ฝฎไฟกๆฏ - ๅฎžๆ—ถ็›‘ๆŽง่„šๆœฌ่ฟ่กŒ็Šถๆ€ 2. **่„šๆœฌๅœๆญขๆŽงๅˆถ**๏ผš - ๆ”ฏๆŒๅผบๅˆถๅœๆญขๆญฃๅœจ่ฟ่กŒ็š„่„šๆœฌ - ไผ˜้›…ๅ…ณ้—ญๅ’Œ่ต„ๆบๆธ…็†ๆœบๅˆถ - ๅœๆญขๅŽ็Šถๆ€ๅ้ฆˆๅ’Œๆ—ฅๅฟ—่ฎฐๅฝ• 3. **่„šๆœฌ้‡ๅฏๅŠŸ่ƒฝ**๏ผš - ๆ”ฏๆŒ่„šๆœฌ็š„้‡ๅฏๆ“ไฝœ - ไฟๆŒ้…็ฝฎๅ‚ๆ•ฐ็š„้‡ๅฏๆœบๅˆถ ### 3.3 ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€ไธŽ่ฎฐๅฝ• 1. **WebSocket ๅฎžๆ—ถๆŽจ้€**๏ผš - ่„šๆœฌๆ‰ง่กŒ่ฟ‡็จ‹ไธญ็š„ๆ ‡ๅ‡†่พ“ๅ‡บๅฎžๆ—ถๆŽจ้€ - ้”™่ฏฏไฟกๆฏๅ’Œๅผ‚ๅธธๅ †ๆ ˆ็š„ๅฎžๆ—ถๆŽจ้€ - ๆ‰ง่กŒ็Šถๆ€ๅ˜ๅŒ–็š„ๅฎžๆ—ถ้€š็Ÿฅ 2. **ๆ—ฅๅฟ—ๅˆ†็บง็ฎก็†**๏ผš - ๆ”ฏๆŒ DEBUGใ€INFOใ€WARNINGใ€ERROR ็ญ‰ๆ—ฅๅฟ—็บงๅˆซ - ๆŒ‰็บงๅˆซ่ฟ‡ๆปคๅ’Œๆ˜พ็คบๆ—ฅๅฟ—ไฟกๆฏ - ๆ—ฅๅฟ—ๆ ผๅผๆ ‡ๅ‡†ๅŒ–ๅ’Œๆ—ถ้—ดๆˆณ่ฎฐๅฝ• 3. **ๆ—ฅๅฟ—ๆŒไน…ๅŒ–ๅญ˜ๅ‚จ**๏ผš - ๆ‰ง่กŒๆ—ฅๅฟ—ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“่กจไธญ - ๆ”ฏๆŒๅކๅฒๆ—ฅๅฟ—ๆŸฅ่ฏขๅ’Œๆฃ€็ดข - ๆ‰ง่กŒ็ป“ๆžœๅ’Œๅผ‚ๅธธไฟกๆฏ็š„็ป“ๆž„ๅŒ–ๅญ˜ๅ‚จ ### 3.4 ไปปๅŠก็ณป็ปŸ้›†ๆˆ่ฐƒ็”จ 1. **VWED ไปปๅŠกๆต็จ‹้›†ๆˆ**๏ผš - ๅœจไปปๅŠกๆต็จ‹ไธญ้€š่ฟ‡่„šๆœฌ็ป„ไปถ่ฐƒ็”จ Python ๅ‡ฝๆ•ฐ - ๆ”ฏๆŒ `SCRIPT` ๅ’Œ `SET_TASK_VARIABLES` ไธค็ง่ฐƒ็”จๆจกๅผ - ไธŽไปปๅŠกๆ‰ง่กŒๅผ•ๆ“Žๆ— ็ผ้›†ๆˆ 2. **ๅ‡ฝๆ•ฐๅŠจๆ€่ฐƒ็”จๆœบๅˆถ**๏ผš ```python # ๅฝ“ๅ‰ๅฎž็Žฐ๏ผšๅ›บๅฎšๅŠ ่ฝฝ scripts/user_save/test1.py script_file = "scripts/user_save/test1.py" # ๅŠจๆ€ๅŠ ่ฝฝๆจกๅ— spec = importlib.util.spec_from_file_location("user_script", script_file) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) # ่ฐƒ็”จๆŒ‡ๅฎšๅ‡ฝๆ•ฐ func = getattr(module, function_name) result = func(function_args) ``` 3. **ๅ‚ๆ•ฐไผ ้€’ๆœบๅˆถ**๏ผš - **ๅญ—ๅ…ธๅ‚ๆ•ฐ**๏ผš`{"key": "value"}` โ†’ ไฝœไธบๅ…ณ้”ฎๅญ—ๅ‚ๆ•ฐไผ ้€’ - **ๅˆ—่กจๅ‚ๆ•ฐ**๏ผš`[arg1, arg2]` โ†’ ไฝœไธบไฝ็ฝฎๅ‚ๆ•ฐไผ ้€’ - **ๅญ—็ฌฆไธฒๅ‚ๆ•ฐ**๏ผšๆ”ฏๆŒ `eval()` ่งฃๆžไธบ Python ๅฏน่ฑก - **ๆ™บ่ƒฝๅ‚ๆ•ฐๅˆคๆ–ญ**๏ผšๆ นๆฎๅ‡ฝๆ•ฐ็ญพๅ่‡ชๅŠจ้€‰ๆ‹ฉไผ ้€’ๆ–นๅผ 4. **ๅผ‚ๆญฅๅ‡ฝๆ•ฐๆ”ฏๆŒ**๏ผš ```python # ๆฃ€ๆต‹ๅผ‚ๆญฅๅ‡ฝๆ•ฐๅนถ็ญ‰ๅพ…็ป“ๆžœ if inspect.iscoroutine(result_value): result_value = await result_value ``` ### 3.5 ๅ˜้‡ไธŠไธ‹ๆ–‡็ฎก็† 1. **ไปปๅŠกๅ˜้‡่ฎพ็ฝฎ**๏ผš - `SET_TASK_VARIABLES` ๆจกๅผ๏ผšๅฐ†ๅ‡ฝๆ•ฐ่ฟ”ๅ›žๅ€ผ่ฎพ็ฝฎไธบไปปๅŠกๅ˜้‡ - ๆ”ฏๆŒๅญ—ๅ…ธ็ป“ๆžœ็š„้”ฎๅ€ผๅฏนๅˆ†ๅˆซ่ฎพ็ฝฎไธบๅ˜้‡ - ้žๅญ—ๅ…ธ็ป“ๆžœ่ฎพ็ฝฎไธบ `scriptResult` ๅ˜้‡ 2. **ๅ˜้‡ๆŒไน…ๅŒ–**๏ผš - ไปปๅŠกๅ˜้‡ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“็š„ `taskrecord` ่กจ - JSON ๆ ผๅผๅญ˜ๅ‚จๅ˜้‡ๆ•ฐๆฎ - ๆ”ฏๆŒ่ทจ็ป„ไปถ็š„ๅ˜้‡ไผ ้€’ๅ’Œๅผ•็”จ 3. **ไธŠไธ‹ๆ–‡ไผ ้€’**๏ผš - `TaskContext` ็ฎก็†ไปปๅŠกๆ‰ง่กŒ่ฟ‡็จ‹ไธญ็š„ๅ˜้‡็Šถๆ€ - ๅ˜้‡ๅœจไปปๅŠกๆต็จ‹ไธญ็š„ไผ ้€’ๅ’Œๆ›ดๆ–ฐ - ๅ˜้‡ไฝœ็”จๅŸŸๅ’Œ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็† --- ## ๅ››ใ€็ปŸไธ€ VWED ๅฏน่ฑกๆจกๅ—็ณป็ปŸ๏ผˆPython ็‰ˆ๏ผ‰ ### 4.1 ่ฎพ่ฎก็†ๅฟต ๆ‰€ๆœ‰ๅŠŸ่ƒฝ้ƒฝ้€š่ฟ‡็ปŸไธ€็š„ `VWED.ๅ†…็ฝฎๅฏน่ฑกๆจกๅ—.ๅ…ทไฝ“ๅ†…็ฝฎๅฏน่ฑก(ๅ‚ๆ•ฐ1๏ผŒๅ‚ๆ•ฐ2)` ๅฝขๅผ่ฐƒ็”จ๏ผŒๅŒ…ๆ‹ฌ๏ผš - ๅ†…็ฝฎๅ‡ฝๆ•ฐๆณจๅ†Œ - ๆŽฅๅฃๆณจๅ†Œ - ไบ‹ไปถ็›‘ๅฌ - ๅฎšๆ—ถไปปๅŠก - ่ฎพๅค‡้€šไฟก - ็ณป็ปŸๆ“ไฝœ ### 4.2 ๆ ธๅฟƒๅฏน่ฑกๆจกๅ—็ป“ๆž„ | ๅฏน่ฑกๆจกๅ— | ่ฏดๆ˜Ž | ็คบไพ‹ | |----------|------|------| | `VWED.api` | ๆŽฅๅฃๆณจๅ†Œๆจกๅ— | `VWED.api.register_route()` | | `VWED.function` | ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐๆณจๅ†Œๆจกๅ— | `VWED.function.register()` | | `VWED.event` | ไบ‹ไปถ็›‘ๅฌๆจกๅ— | `VWED.event.listen()` | | `VWED.task` | ไปปๅŠก็ฎก็†ๆจกๅ— | `VWED.task.create()` | | `VWED.device` | ่ฎพๅค‡้€šไฟกๆจกๅ— | `VWED.device.modbus()` | | `VWED.robot` | ๆœบๅ™จไบบๆŽงๅˆถๆจกๅ— | `VWED.robot.get_status()` | | `VWED.timer` | ๅฎšๆ—ถไปปๅŠกๆจกๅ— | `VWED.timer.schedule()` | | `VWED.log` | ๆ—ฅๅฟ—่พ“ๅ‡บๆจกๅ— | `VWED.log.info()` | | `VWED.http` | HTTP ่ฏทๆฑ‚ๆจกๅ— | `VWED.http.post()` | | `VWED.storage` | ๅบ“ไฝ็ฎก็†ๆจกๅ— | `VWED.storage.get_location()` | | `VWED.mail` | ้‚ฎไปถๅ‘้€ๆจกๅ— | `VWED.mail.send()` | ### 4.3 ็ปŸไธ€ๅฏน่ฑกๆจกๅ— API #### 4.3.1 ๆŽฅๅฃๆณจๅ†Œๆจกๅ— (VWED.api) ```python # ๆณจๅ†Œ HTTP ๆŽฅๅฃ VWED.api.register_route(path="/script-api/move", method="POST", handler=move_handler) # ๆณจๅ†Œ WebSocket ๆŽฅๅฃ VWED.api.register_websocket(path="/ws/script", handler=ws_handler) # ๆณจๅ†Œ TCP ๆŽฅๅฃ VWED.api.register_tcp(port=9999, handler=tcp_handler) ``` #### 4.3.2 ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐๆณจๅ†Œๆจกๅ— (VWED.function) ```python # ๆณจๅ†Œ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐ VWED.function.register(name="calculate_distance", handler=calc_distance_func, description="่ฎก็ฎ—ไธค็‚น้—ด่ท็ฆป", params=["point1", "point2"]) # ๆณจๅ†ŒไธšๅŠก้€ป่พ‘ๅ‡ฝๆ•ฐ VWED.function.register(name="warehouse_allocation", handler=allocate_warehouse, description="ไป“ๅบ“ๅˆ†้…้€ป่พ‘", params=["goods_type", "quantity"]) # ๆณจๅ†Œๆ•ฐๆฎๅค„็†ๅ‡ฝๆ•ฐ VWED.function.register(name="format_sensor_data", handler=format_data_func, description="ๆ ผๅผๅŒ–ไผ ๆ„Ÿๅ™จๆ•ฐๆฎ", params=["raw_data"]) # ่Žทๅ–ๅทฒๆณจๅ†Œๅ‡ฝๆ•ฐๅˆ—่กจ functions = VWED.function.list_all() # ่ฐƒ็”จๆณจๅ†Œ็š„ๅ‡ฝๆ•ฐ๏ผˆๅ†…้ƒจไฝฟ็”จ๏ผŒ้€šๅธธๅœจscript.pyไธญ่ฐƒ็”จ๏ผ‰ result = VWED.function.call(name="calculate_distance", args={"point1": [0,0], "point2": [3,4]}) ``` #### 4.3.3 ไบ‹ไปถ็›‘ๅฌๆจกๅ— (VWED.event) ```python # ็›‘ๅฌ็ณป็ปŸไบ‹ไปถ VWED.event.listen(event_name="task_done", handler=on_task_done) # ็›‘ๅฌ่‡ชๅฎšไน‰ไบ‹ไปถ VWED.event.listen(event_name="custom_event", handler=on_custom) # ่งฆๅ‘ไบ‹ไปถ VWED.event.emit(event_name="custom_event", data={"msg": "test"}) ``` #### 4.3.3 ไปปๅŠก็ฎก็†ๆจกๅ— (VWED.task) ```python # ๅˆ›ๅปบไปปๅŠก VWED.task.create(task_label="move", input_params={"to": "A001"}) # ่Žทๅ–ไปปๅŠก็Šถๆ€ VWED.task.get_status(task_id=123) # ๅœๆญขไปปๅŠก VWED.task.stop(task_id=123) ``` #### 4.3.4 ่ฎพๅค‡้€šไฟกๆจกๅ— (VWED.device) ```python # Modbus ้€šไฟก VWED.device.modbus(ip="192.168.1.100", port=502, register=100, value=1) # OPC ้€šไฟก VWED.device.opc(server_url="opc.tcp://127.0.0.1:4840", node_id="ns=2;i=2") # PLC ้€šไฟก VWED.device.plc(ip="192.168.1.50", rack=0, slot=1, db_number=1, start=0, size=4) ``` #### 4.3.5 ๆœบๅ™จไบบๆŽงๅˆถๆจกๅ— (VWED.robot) ```python # ่Žทๅ–ๆœบๅ™จไบบ็Šถๆ€ VWED.robot.get_status(robot_id="robot_001") # ่Žทๅ–ๆ‰€ๆœ‰ๆœบๅ™จไบบ VWED.robot.get_all() # ่ฐƒๅบฆๆœบๅ™จไบบ VWED.robot.dispatch(robot_id="robot_001", task_id=123) ``` #### 4.3.6 ๅฎšๆ—ถไปปๅŠกๆจกๅ— (VWED.timer) ```python # ๅฎšๆ—ถๆ‰ง่กŒ VWED.timer.schedule(interval=5, handler=timer_handler, repeat=True) # ๅปถๆ—ถๆ‰ง่กŒ VWED.timer.delay(seconds=10, handler=delay_handler) # ๅ–ๆถˆๅฎšๆ—ถไปปๅŠก VWED.timer.cancel(timer_id="timer_001") ``` #### 4.3.7 ๆ—ฅๅฟ—ๆจกๅ— (VWED.log) ```python # ๅ„็งๆ—ฅๅฟ—็บงๅˆซ VWED.log.info(tag="script", message="่„šๆœฌๅฏๅŠจๆˆๅŠŸ") VWED.log.debug(tag="debug", message="่ฐƒ่ฏ•ไฟกๆฏ") VWED.log.warning(tag="warn", message="่ญฆๅ‘Šไฟกๆฏ") VWED.log.error(tag="error", message="้”™่ฏฏไฟกๆฏ") ``` #### 4.3.8 HTTP ่ฏทๆฑ‚ๆจกๅ— (VWED.http) ```python # GET ่ฏทๆฑ‚ VWED.http.get(url="http://api.example.com/data", headers={}) # POST ่ฏทๆฑ‚ VWED.http.post(url="http://api.example.com/submit", json={"key": "value"}) # PUT/DELETE ่ฏทๆฑ‚ VWED.http.put(url="http://api.example.com/update", json={}) VWED.http.delete(url="http://api.example.com/delete") ``` #### 4.3.9 ๅบ“ไฝ็ฎก็†ๆจกๅ— (VWED.storage) ```python # ่Žทๅ–ๅบ“ไฝไฟกๆฏ VWED.storage.get_location(location_code="A001") # ้”ๅฎšๅบ“ไฝ VWED.storage.lock_location(location_code="A001", task_id=123) # ้‡Šๆ”พๅบ“ไฝ VWED.storage.unlock_location(location_code="A001") ``` #### 4.3.10 ้‚ฎไปถๆจกๅ— (VWED.mail) ```python # ๅ‘้€้‚ฎไปถ VWED.mail.send(to="admin@example.com", subject="ๅ‘Š่ญฆ", body="็ณป็ปŸๅผ‚ๅธธ") # ๅ‘้€ๅธฆ้™„ไปถ้‚ฎไปถ VWED.mail.send_with_attachment(to="admin@example.com", subject="ๆŠฅๅ‘Š", body="่ฏทๆŸฅ็œ‹้™„ไปถ", attachment_path="/tmp/report.pdf") ``` ### 4.4 ๆณจๅ†Œๆธ…็†ๆœบๅˆถ ```python # ่„šๆœฌๅœๆญขๆ—ถ่‡ชๅŠจๆธ…็†ๆ‰€ๆœ‰ๆณจๅ†Œ้กน def script_stop_cleanup(): """่„šๆœฌๅœๆญขๆ—ถ่ฐƒ็”จ๏ผŒๆธ…็†ๆ‰€ๆœ‰ๆณจๅ†Œ็š„ๅ†…ๅฎน""" VWED.api.clear_all() # ๆธ…็†ๆŽฅๅฃๆณจๅ†Œ VWED.event.clear_all() # ๆธ…็†ไบ‹ไปถ็›‘ๅฌ VWED.timer.clear_all() # ๆธ…็†ๅฎšๆ—ถไปปๅŠก VWED.log.info("script", "่„šๆœฌๆณจๅ†Œ้กนๅทฒๅ…จ้ƒจๆธ…็†") ``` --- ## ไบ”ใ€่ฐƒ่ฏ•่ƒฝๅŠ›๏ผˆPython ็‰ˆ๏ผ‰ - ๆ”ฏๆŒ `remote_pdb.set_trace()` - ๅฏๅŠจ่„šๆœฌๆ—ถ่‡ชๅŠจๅผ€ๅฏ่ฐƒ่ฏ•็ซฏๅฃ๏ผˆๅฆ‚ 5555๏ผ‰ - ๅ‰็ซฏๅผนๅ‡บ่ฐƒ่ฏ•ๅœฐๅ€๏ผš`telnet 127.0.0.1 5555` - ๆ”ฏๆŒๆ–ญ็‚นใ€ๅ˜้‡ๆŸฅ็œ‹ใ€ๅ•ๆญฅๆ‰ง่กŒ --- ## ๅ…ญใ€่„šๆœฌ็›ฎๅฝ•็ป“ๆž„๏ผˆPython ็‰ˆ๏ผ‰ ``` data/VWED/python_script/ โ”œโ”€โ”€ common/ # ้€š็”จๅทฅๅ…ท็›ฎๅฝ• โ”‚ โ”œโ”€โ”€ utils.py # ้€š็”จๅทฅๅ…ท่„šๆœฌ โ”‚ โ””โ”€โ”€ config.py # ้…็ฝฎ็ฎก็†่„šๆœฌ โ”œโ”€โ”€ warehouse/ # ไป“ๅบ“็ฎก็†็›ฎๅฝ• โ”‚ โ”œโ”€โ”€ controller.py # ไป“ๅบ“ๆŽงๅˆถ่„šๆœฌ โ”‚ โ””โ”€โ”€ allocation.py # ๅบ“ไฝๅˆ†้…่„šๆœฌ โ”œโ”€โ”€ robot/ # ๆœบๅ™จไบบๆŽงๅˆถ็›ฎๅฝ• โ”‚ โ”œโ”€โ”€ dispatcher.py # ๆœบๅ™จไบบ่ฐƒๅบฆ่„šๆœฌ โ”‚ โ””โ”€โ”€ monitor.py # ๆœบๅ™จไบบ็›‘ๆŽง่„šๆœฌ โ”œโ”€โ”€ sensors/ # ไผ ๆ„Ÿๅ™จๅค„็†็›ฎๅฝ• โ”‚ โ”œโ”€โ”€ data_processor.py # ๆ•ฐๆฎๅค„็†่„šๆœฌ โ”‚ โ””โ”€โ”€ alarm_handler.py # ๅ‘Š่ญฆๅค„็†่„šๆœฌ โ””โ”€โ”€ boot.py # ไธปๅฏๅŠจ่„šๆœฌ ``` ### ๅˆ›ๅปบ่ง„ๅˆ™ - **ๅˆ›ๅปบ็›ฎๅฝ•**๏ผš`create_script(directory="ๆ–ฐ็›ฎๅฝ•ๅ")` - **ๅˆ›ๅปบๆ–‡ไปถ**๏ผš`create_script(directory="ๆ‰€ๅฑž็›ฎๅฝ•", filename="่„šๆœฌๅ.py")` ๆณจๆ„๏ผšๆ‰€ๆœ‰็”จๆˆทๅ…ฑไบซๅŒไธ€ๅฅ—่„šๆœฌๆ–‡ไปถ๏ผŒไปปไฝ•็”จๆˆท็š„ไฟฎๆ”น้ƒฝไผšๅฝฑๅ“ๆ‰€ๆœ‰็”จๆˆทใ€‚ ``` ## ๅ…ซใ€ๅ…ฑไบซ่„šๆœฌๆžถๆž„ ### 8.1 ๅ•็”จๆˆทๅ…ฑไบซๆจกๅผๆžถๆž„ ```text โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅ…จๅฑ€ๆณจๅ†Œไธญๅฟƒ โ”‚ โ”‚ - ็ปŸไธ€API่ทฏ็”ฑ - ็ปŸไธ€ๅ‡ฝๆ•ฐๆณจๅ†Œ - ็ปŸไธ€ไบ‹ไปถ็›‘ๅฌ โ”‚ โ”‚ - ๆ‰€ๆœ‰็”จๆˆทๅ…ฑไบซ - ่„šๆœฌ็‰ˆๆœฌๆŽงๅˆถ - ๆœ€ๅŽไฟฎๆ”น่ฆ†็›– โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ็ปŸไธ€่„šๆœฌๅญ˜ๅ‚จ โ”‚ โ”‚ data/VWED/python_script/ โ”‚ โ”‚ โ”œโ”€โ”€ boot.py โ”‚ โ”‚ โ”œโ”€โ”€ task_manager.py โ”‚ โ”‚ โ”œโ”€โ”€ sensor_processor.py โ”‚ โ”‚ โ””โ”€โ”€ utils.py โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### 8.2 ๅคš่„šๆœฌๆœๅŠกๅผ•ๆ“Ž ```python class VWEDGlobalRegistry: """VWED ๅ…จๅฑ€ๆณจๅ†Œไธญๅฟƒ๏ผˆๆ”ฏๆŒๅคš่„šๆœฌ็‹ฌ็ซ‹ๆœๅŠก๏ผ‰""" def __init__(self): self.api_routes = {} # HTTPๆŽฅๅฃๆณจๅ†Œ: {route_key: route_info} self.websocket_routes = {} # WebSocketๆŽฅๅฃๆณจๅ†Œ self.tcp_servers = {} # TCPๆœๅŠกๆณจๅ†Œ self.event_listeners = {} # ไบ‹ไปถ็›‘ๅฌๅ™จๆณจๅ†Œ: {event_name: [listener_info]} self.timers = {} # ๅฎšๆ—ถไปปๅŠกๆณจๅ†Œ self.registered_functions = {} # ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐๆณจๅ†Œ: {function_name: function_info} self.active_scripts = {} # ๆดป่ทƒ่„šๆœฌ: {script_id: script_info} self.current_script_id = None # ๅฝ“ๅ‰ๆ‰ง่กŒ็š„่„šๆœฌID def register_api_route(self, path: str, method: str, handler, script_id: str = None): """ๆณจๅ†ŒAPIๆŽฅๅฃ๏ผˆๆŒ‰่„šๆœฌ้š”็ฆป๏ผ‰""" script_id = script_id or self.current_script_id route_key = f"{method}:{path}" self.api_routes[route_key] = { "path": path, "method": method, "handler": handler, "script_id": script_id, "registered_at": datetime.now().isoformat() } def register_function(self, name: str, handler, script_id: str = None, description: str = "", params: list = None): """ๆณจๅ†Œ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐ๏ผˆๆŒ‰่„šๆœฌ้š”็ฆป๏ผ‰""" script_id = script_id or self.current_script_id self.registered_functions[name] = { "handler": handler, "script_id": script_id, "description": description, "params": params or [], "registered_at": datetime.now().isoformat() } def register_event_listener(self, event_name: str, handler, script_id: str = None, priority: int = 1): """ๆณจๅ†Œไบ‹ไปถ็›‘ๅฌๅ™จ๏ผˆๆŒ‰่„šๆœฌ้š”็ฆป๏ผ‰""" script_id = script_id or self.current_script_id if event_name not in self.event_listeners: self.event_listeners[event_name] = [] self.event_listeners[event_name].append({ "handler": handler, "script_id": script_id, "priority": priority, "registered_at": datetime.now().isoformat() }) def get_api_route(self, path: str, method: str = "GET"): """่Žทๅ–API่ทฏ็”ฑ""" route_key = f"{method}:{path}" return self.api_routes.get(route_key) def get_registered_function(self, name: str): """่Žทๅ–ๆณจๅ†Œ็š„ๅ‡ฝๆ•ฐ""" func_info = self.registered_functions.get(name) return func_info["handler"] if func_info else None def clear_script_registrations(self, script_id: str): """ๆธ…็†็‰นๅฎš่„šๆœฌ็š„ๆ‰€ๆœ‰ๆณจๅ†Œ้กน""" # ๆธ…็†API่ทฏ็”ฑ api_routes_to_remove = [k for k, v in self.api_routes.items() if v.get("script_id") == script_id] for route_key in api_routes_to_remove: del self.api_routes[route_key] # ๆธ…็†ๅ‡ฝๆ•ฐๆณจๅ†Œ functions_to_remove = [k for k, v in self.registered_functions.items() if v.get("script_id") == script_id] for func_name in functions_to_remove: del self.registered_functions[func_name] # ๆธ…็†ไบ‹ไปถ็›‘ๅฌๅ™จ for event_name in self.event_listeners: self.event_listeners[event_name] = [ listener for listener in self.event_listeners[event_name] if listener.get("script_id") != script_id ] # ๆธ…็†ๅฎšๆ—ถไปปๅŠก timers_to_remove = [k for k, v in self.timers.items() if v.get("script_id") == script_id] for timer_id in timers_to_remove: del self.timers[timer_id] # ไปŽๆดป่ทƒ่„šๆœฌๅˆ—่กจ็งป้™ค if script_id in self.active_scripts: del self.active_scripts[script_id] def clear_all(self): """ๆธ…็†ๆ‰€ๆœ‰ๆณจๅ†Œ้กน""" self.api_routes.clear() self.websocket_routes.clear() self.tcp_servers.clear() self.event_listeners.clear() self.timers.clear() self.registered_functions.clear() self.active_scripts.clear() def set_current_script(self, script_id: str): """่ฎพ็ฝฎๅฝ“ๅ‰ๆ‰ง่กŒ็š„่„šๆœฌID""" self.current_script_id = script_id def add_active_script(self, script_id: str, script_path: str): """ๆทปๅŠ ๆดป่ทƒ่„šๆœฌ่ฎฐๅฝ•""" self.active_scripts[script_id] = { "script_path": script_path, "started_at": datetime.now().isoformat(), "status": "running" } def get_script_registrations(self, script_id: str): """่Žทๅ–็‰นๅฎš่„šๆœฌ็š„ๆณจๅ†Œ็ปŸ่ฎก""" api_count = len([v for v in self.api_routes.values() if v.get("script_id") == script_id]) function_count = len([v for v in self.registered_functions.values() if v.get("script_id") == script_id]) event_count = sum([ len([l for l in listeners if l.get("script_id") == script_id]) for listeners in self.event_listeners.values() ]) timer_count = len([v for v in self.timers.values() if v.get("script_id") == script_id]) return { "apis": api_count, "functions": function_count, "events": event_count, "timers": timer_count } # ๅ…จๅฑ€ๆณจๅ†Œๅฎžไพ‹ _global_registry = VWEDGlobalRegistry() class MultiScriptEngine: """ๅคš่„šๆœฌๆœๅŠกๅผ•ๆ“Ž""" def __init__(self): self.script_store = GlobalScriptStore() self.running_scripts = {} # {script_id: script_info} def start_script_service(self, script_path: str): """ๅฏๅŠจ่„šๆœฌๆœๅŠก - ๆ”ฏๆŒๅคš่„šๆœฌๅนถๅ‘""" try: # 1. ้ชŒ่ฏ่„šๆœฌๆ–‡ไปถๅญ˜ๅœจ full_script_path = f"data/VWED/python_script/{script_path}" if not os.path.exists(full_script_path): return {"success": False, "error": f"่„šๆœฌๆ–‡ไปถไธๅญ˜ๅœจ: {script_path}"} # 2. ็”Ÿๆˆๅ”ฏไธ€่„šๆœฌID script_id = f"{script_path}_{int(time.time() * 1000)}" # 3. ๆฃ€ๆŸฅๆ˜ฏๅฆๅทฒๆœ‰ๅŒๅ่„šๆœฌๅœจ่ฟ่กŒ existing_script = self.find_running_script_by_path(script_path) if existing_script: return { "success": False, "error": f"่„šๆœฌ {script_path} ๅทฒๅœจ่ฟ่กŒไธญ๏ผŒscript_id: {existing_script['script_id']}" } # 4. ่ฎพ็ฝฎๅ…จๅฑ€ๆณจๅ†ŒไธญๅฟƒไธŠไธ‹ๆ–‡ _global_registry.set_current_script(script_id) _global_registry.add_active_script(script_id, script_path) # 5. ๆณจๅ…ฅVWEDๆจกๅ— script_globals = { 'VWED': VWEDObjectFactory(), '__name__': '__main__' } # 6. ๆ‰ง่กŒ่„šๆœฌ with open(full_script_path, 'r', encoding='utf-8') as f: script_code = f.read() exec(script_code, script_globals) # 7. ่ฐƒ็”จbootๅ‡ฝๆ•ฐๆณจๅ†ŒๆœๅŠก if 'boot' in script_globals: script_globals['boot']() else: _global_registry.clear_script_registrations(script_id) return {"success": False, "error": "่„šๆœฌ็ผบๅฐ‘boot()ๅ…ฅๅฃๅ‡ฝๆ•ฐ"} # 8. ่ฎฐๅฝ•่ฟ่กŒไธญ็š„่„šๆœฌ self.running_scripts[script_id] = { "script_path": script_path, "started_at": datetime.now().isoformat(), "status": "running" } # 9. ไฟๅญ˜่„šๆœฌๅˆฐๆ•ฐๆฎๅบ“ self.script_store.save_script(script_path, script_code) # 10. ่Žทๅ–ๆณจๅ†Œ็ปŸ่ฎก registrations = _global_registry.get_script_registrations(script_id) return { "success": True, "script_id": script_id, "message": f"่„šๆœฌๆœๅŠกๅฏๅŠจๆˆๅŠŸ๏ผŒๅทฒๆณจๅ†Œ {registrations['apis']} ไธชๆŽฅๅฃ๏ผŒ{registrations['functions']} ไธชๅ‡ฝๆ•ฐ๏ผŒ{registrations['events']} ไธชไบ‹ไปถ็›‘ๅฌๅ™จ" } except Exception as e: if script_id: _global_registry.clear_script_registrations(script_id) return {"success": False, "error": f"่„šๆœฌๆœๅŠกๅฏๅŠจๅคฑ่ดฅ: {str(e)}"} def stop_script_service(self, script_id: str): """ๅœๆญข็‰นๅฎš่„šๆœฌๆœๅŠก - ๅ…ณ้”ฎๅŠŸ่ƒฝ""" try: if script_id not in self.running_scripts: return {"success": False, "error": f"่„šๆœฌ {script_id} ๆœช่ฟ่กŒ"} # ่Žทๅ–ๆณจๅ†Œ็ปŸ่ฎก registrations = _global_registry.get_script_registrations(script_id) # ๆธ…็†่ฏฅ่„šๆœฌ็š„ๆ‰€ๆœ‰ๆณจๅ†Œ้กน - ่ฟ™ๆ˜ฏๅ…ณ้”ฎๆญฅ้ชค _global_registry.clear_script_registrations(script_id) # ไปŽ่ฟ่กŒๅˆ—่กจ็งป้™ค script_info = self.running_scripts.pop(script_id) return { "success": True, "message": f"่„šๆœฌๆœๅŠกๅทฒๅœๆญข ({script_info['script_path']})๏ผŒๅทฒๆธ…็† {registrations['apis']} ไธชๆŽฅๅฃใ€{registrations['functions']} ไธชๅ‡ฝๆ•ฐใ€{registrations['events']} ไธชไบ‹ไปถ็›‘ๅฌๅ™จ" } except Exception as e: return {"success": False, "error": f"ๅœๆญข่„šๆœฌๆœๅŠกๅคฑ่ดฅ: {str(e)}"} def get_running_scripts(self): """่Žทๅ–ๆ‰€ๆœ‰่ฟ่กŒไธญ็š„่„šๆœฌ""" result = [] for script_id, script_info in self.running_scripts.items(): registrations = _global_registry.get_script_registrations(script_id) result.append({ "script_id": script_id, "script_path": script_info["script_path"], "started_at": script_info["started_at"], "status": script_info["status"], "registrations": registrations }) return result def find_running_script_by_path(self, script_path: str): """ๆ นๆฎ่„šๆœฌ่ทฏๅพ„ๆŸฅๆ‰พ่ฟ่กŒไธญ็š„่„šๆœฌ""" for script_id, script_info in self.running_scripts.items(): if script_info["script_path"] == script_path: return {"script_id": script_id, **script_info} return None def create_script(self, directory: str, filename: str = None): """ๅˆ›ๅปบ่„šๆœฌๆˆ–็›ฎๅฝ•""" try: base_path = "data/VWED/python_script" if filename is None: # ๅˆ›ๅปบ็›ฎๅฝ• dir_path = os.path.join(base_path, directory) os.makedirs(dir_path, exist_ok=True) return {"success": True, "message": f"็›ฎๅฝ•ๅˆ›ๅปบๆˆๅŠŸ: {directory}"} else: # ๅˆ›ๅปบๆ–‡ไปถ dir_path = os.path.join(base_path, directory) os.makedirs(dir_path, exist_ok=True) file_path = os.path.join(dir_path, filename) if not filename.endswith('.py'): filename += '.py' file_path += '.py' # ๅˆ›ๅปบ้ป˜่ฎค่„šๆœฌๆจกๆฟ default_content = f'''def boot(): """่„šๆœฌๅ…ฅๅฃๅ‡ฝๆ•ฐ""" VWED.log.info("script", "่„šๆœฌ {filename} ๅฏๅŠจๆˆๅŠŸ") # ๅœจ่ฟ™้‡Œๆณจๅ†Œๆ‚จ็š„ๆŽฅๅฃใ€ๅ‡ฝๆ•ฐๅ’Œไบ‹ไปถ็›‘ๅฌๅ™จ # ็คบไพ‹๏ผš # VWED.api.register_route("/test", "GET", test_handler) # VWED.function.register("test_func", test_function) pass def test_handler(request): """็คบไพ‹APIๅค„็†ๅ‡ฝๆ•ฐ""" return {{"message": "Hello from script API", "script": "{filename}"}} def test_function(args): """็คบไพ‹่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐ""" return {{"result": "Function executed", "args": args}} ''' with open(file_path, 'w', encoding='utf-8') as f: f.write(default_content) return {"success": True, "message": f"ๆ–‡ไปถๅˆ›ๅปบๆˆๅŠŸ: {directory}/{filename}"} except Exception as e: return {"success": False, "error": f"ๅˆ›ๅปบๅคฑ่ดฅ: {str(e)}"} class GlobalScriptStore: """ๅ…จๅฑ€่„šๆœฌๅญ˜ๅ‚จ็ฎก็†""" def save_script(self, script_name: str, content: str): """ไฟๅญ˜่„šๆœฌๅˆฐๆ–‡ไปถ็ณป็ปŸๅ’Œๆ•ฐๆฎๅบ“""" # 1. ไฟๅญ˜ๅˆฐๆ–‡ไปถ็ณป็ปŸ script_path = f"data/VWED/python_script/{script_name}" with open(script_path, 'w', encoding='utf-8') as f: f.write(content) # 2. ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“๏ผˆ็‰ˆๆœฌๆŽงๅˆถ๏ผŒไฝ†ๅชไฟ็•™ๆœ€ๆ–ฐ็‰ˆๆœฌ๏ผ‰ self._save_to_database(script_name, content) def _save_to_database(self, script_name: str, content: str): """ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“๏ผˆ่ฆ†็›–ๆ—ง็‰ˆๆœฌ๏ผ‰""" # ่ฟ™้‡Œๅฎž็Žฐๆ•ฐๆฎๅบ“ไฟๅญ˜้€ป่พ‘ # ็ณป็ปŸๅชไฟ็•™ๆœ€ๅŽไธ€ไธช็‰ˆๆœฌ๏ผŒไธ่€ƒ่™‘ๅคš็‰ˆๆœฌ็ฎก็† pass ``` ### 8.3 ๅคš่„šๆœฌๆœๅŠก่ฎฟ้—ฎๆŽงๅˆถๆœบๅˆถ ```python # FastAPI ๅคš่„šๆœฌ่ทฏ็”ฑๅค„็†ๅ™จ @app.api_route("/script-api/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) async def multi_script_route(path: str, request: Request): """ๅคš่„šๆœฌ่ทฏ็”ฑๅค„็†ๅ™จ""" # 1. ๆฃ€ๆŸฅๆ˜ฏๅฆๆœ‰ไปปไฝ•่„šๆœฌๅœจ่ฟ่กŒ if not multi_script_engine.running_scripts: return {"error": "ๆฒกๆœ‰่„šๆœฌๆœๅŠกๅœจ่ฟ่กŒ๏ผŒ่ฏทๅ…ˆๅฏๅŠจ่„šๆœฌๆœๅŠก"} # 2. ไปŽๅ…จๅฑ€ๆณจๅ†Œไธญๅฟƒ่Žทๅ–่ทฏ็”ฑ api_path = "/" + path route_info = _global_registry.get_api_route(api_path, request.method) if route_info: # 3. ๆฃ€ๆŸฅ่ฏฅ่ทฏ็”ฑๅฏนๅบ”็š„่„šๆœฌๆ˜ฏๅฆไปๅœจ่ฟ่กŒ script_id = route_info.get("script_id") if script_id not in multi_script_engine.running_scripts: return {"error": f"่ทฏ็”ฑ {api_path} ๅฏนๅบ”็š„่„šๆœฌๆœๅŠกๅทฒๅœๆญข"} try: handler = route_info["handler"] # ๆž„ๅปบ่ฏทๆฑ‚ๆ•ฐๆฎ request_data = { "method": request.method, "path": path, "script_id": script_id, # ๆทปๅŠ ่„šๆœฌIDไฟกๆฏ "headers": dict(request.headers), "query_params": dict(request.query_params) } if request.method in ["POST", "PUT", "PATCH"]: try: request_data["json"] = await request.json() except: request_data["body"] = await request.body() # ่ฐƒ็”จๅค„็†ๅ‡ฝๆ•ฐ if asyncio.iscoroutinefunction(handler): result = await handler(request_data) else: result = handler(request_data) return result except Exception as e: return {"error": f"่„šๆœฌๆŽฅๅฃๆ‰ง่กŒๅคฑ่ดฅ: {str(e)}"} else: return {"error": f"ๆŽฅๅฃ {request.method} {api_path} ๆœชๆณจๅ†Œ"} # ่„šๆœฌ็ฎก็†API @app.get("/script-engine/running") async def get_running_scripts(): """่Žทๅ–ๆ‰€ๆœ‰่ฟ่กŒไธญ็š„่„šๆœฌ""" return multi_script_engine.get_running_scripts() @app.post("/script-engine/start") async def start_script_service(request: dict): """ๅฏๅŠจ่„šๆœฌๆœๅŠก""" script_path = request.get("script_path") if not script_path: return {"success": False, "error": "็ผบๅฐ‘script_pathๅ‚ๆ•ฐ"} return multi_script_engine.start_script_service(script_path) @app.post("/script-engine/stop") async def stop_script_service(request: dict): """ๅœๆญข่„šๆœฌๆœๅŠก""" script_id = request.get("script_id") if not script_id: return {"success": False, "error": "็ผบๅฐ‘script_idๅ‚ๆ•ฐ"} return multi_script_engine.stop_script_service(script_id) ``` ## ไนใ€ๅ…ณ้”ฎๅŠŸ่ƒฝ่ฏดๆ˜Ž ### 9.1 ๅคš่„šๆœฌๆœๅŠกๅฏๅŠจ็š„้‡่ฆๆ€ง ๅคš่„šๆœฌๆœๅŠกๅฏๅŠจๆ˜ฏๆ•ดไธช่„šๆœฌๅผ•ๆ“Ž็š„ๆ ธๅฟƒๅŠŸ่ƒฝ๏ผš 1. **็‹ฌ็ซ‹ๆณจๅ†Œ**๏ผšๆฏไธช่„šๆœฌๅฏๅŠจๆ—ถ็”Ÿๆˆๅ”ฏไธ€็š„script_id๏ผŒๅ…ถๆŽฅๅฃใ€ๅ‡ฝๆ•ฐใ€ไบ‹ไปถๅธฆๆœ‰่ฏฅIDๆ ‡่ฎฐๆณจๅ†Œๅˆฐๅ…จๅฑ€ๆณจๅ†Œไธญๅฟƒ 2. **ๅนถๅ‘่ฟ่กŒ**๏ผšๆ”ฏๆŒๅคšไธช่„šๆœฌๅŒๆ—ถ่ฟ่กŒๆœๅŠก๏ผŒๆฏไธช่„šๆœฌ็š„่ต„ๆบ็›ธไบ’็‹ฌ็ซ‹ 3. **ๅค–้ƒจ่ฎฟ้—ฎๆŽงๅˆถ**๏ผšๅชๆœ‰่ฟ่กŒไธญ็š„่„šๆœฌ่ต„ๆบๆ‰่ƒฝ่ขซๅค–้ƒจ็ณป็ปŸ่ฎฟ้—ฎ 4. **้˜ฒ้‡ๅคๅฏๅŠจ**๏ผšๅŒไธ€่„šๆœฌ่ทฏๅพ„ไธ่ƒฝ้‡ๅคๅฏๅŠจ๏ผŒ็กฎไฟ่ต„ๆบ็š„ๅ”ฏไธ€ๆ€ง ### 9.2 ๆŒ‰่„šๆœฌIDๆธ…็†็š„ๅ…ณ้”ฎไฝœ็”จ ๅœๆญข็‰นๅฎš่„šๆœฌๆœๅŠกๆ˜ฏ็ฒพ็กฎ่ต„ๆบๆธ…็†็š„ๅ…ณ้”ฎ๏ผš 1. **็ฒพ็กฎๆธ…็†**๏ผšๆ นๆฎscript_idๅชๆธ…็†่ฏฅ่„šๆœฌๆณจๅ†Œ็š„ๆŽฅๅฃใ€ๅ‡ฝๆ•ฐใ€ไบ‹ไปถๅ’Œๅฎšๆ—ถไปปๅŠก 2. **ๅ…ถไป–ๆœๅŠกไธๅ—ๅฝฑๅ“**๏ผšๅœๆญขๆŸไธช่„šๆœฌไธไผšๅฝฑๅ“ๅ…ถไป–ๆญฃๅœจ่ฟ่กŒ็š„่„šๆœฌๆœๅŠก 3. **่ฎฟ้—ฎๆŽงๅˆถ**๏ผš่ฏฅ่„šๆœฌ็š„่ต„ๆบ็ซ‹ๅณไธๅฏ่ฎฟ้—ฎ๏ผŒไฝ†ๅ…ถไป–่„šๆœฌ็š„่ต„ๆบ็ปง็ปญๆญฃๅธธๆœๅŠก 4. **็Šถๆ€ไธ€่‡ดๆ€ง**๏ผš็กฎไฟๆณจๅ†Œไธญๅฟƒ็š„่ต„ๆบ็Šถๆ€ไธŽๅฎž้™…่ฟ่กŒ่„šๆœฌไฟๆŒๅŒๆญฅ ### 9.3 ๅคš่„šๆœฌ่ต„ๆบ้š”็ฆปๆœบๅˆถ ๆฏไธช่„šๆœฌ็š„่ต„ๆบๅฎŒๅ…จ็‹ฌ็ซ‹๏ผš 1. **ๆŽฅๅฃ้š”็ฆป**๏ผšไธๅŒ่„šๆœฌๅฏไปฅๆณจๅ†Œ็›ธๅŒ่ทฏๅพ„็š„ๆŽฅๅฃ๏ผŒ้€š่ฟ‡script_idๅŒบๅˆ† 2. **ๅ‡ฝๆ•ฐ้š”็ฆป**๏ผšไธๅŒ่„šๆœฌ็š„ๅŒๅๅ‡ฝๆ•ฐ้€š่ฟ‡script_id็ฎก็†๏ผŒๅŽๆณจๅ†Œ็š„ไผš่ฆ†็›–ๅ‰้ข็š„ 3. **ไบ‹ไปถ้š”็ฆป**๏ผšไบ‹ไปถ็›‘ๅฌๅ™จๆŒ‰script_idๅˆ†็ป„๏ผŒๅœๆญข่„šๆœฌๆ—ถ็ฒพ็กฎๆธ…็†ๅฏนๅบ”็›‘ๅฌๅ™จ 4. **ๅฎšๆ—ถไปปๅŠก้š”็ฆป**๏ผšๅฎšๆ—ถไปปๅŠกๅธฆๆœ‰script_idๆ ‡่ฎฐ๏ผŒๆ”ฏๆŒๆŒ‰่„šๆœฌ็‹ฌ็ซ‹็ฎก็† ### 9.4 ็›ฎๅฝ•ๅ’Œๆ–‡ไปถๅˆ›ๅปบๆœบๅˆถ ๆ”ฏๆŒ็ตๆดป็š„่„šๆœฌ็ป„็ป‡็ป“ๆž„๏ผš 1. **็›ฎๅฝ•ๅˆ›ๅปบ**๏ผš`create_script(directory="ๆ–ฐ็›ฎๅฝ•ๅ")` 2. **ๆ–‡ไปถๅˆ›ๅปบ**๏ผš`create_script(directory="ๆ‰€ๅฑž็›ฎๅฝ•", filename="่„šๆœฌๅ.py")` 3. **ๆจกๆฟ็”Ÿๆˆ**๏ผšๆ–ฐๅˆ›ๅปบ็š„่„šๆœฌๆ–‡ไปถๅŒ…ๅซ้ป˜่ฎค็š„boot()ๅ‡ฝๆ•ฐๅ’Œ็คบไพ‹ไปฃ็  ## ๅใ€ๅคš่„šๆœฌๆœๅŠก็คบไพ‹ ### 10.1 ๅนถๅ‘่ฟ่กŒ็คบไพ‹ ```python # ่„šๆœฌA: warehouse/controller.py def boot(): VWED.api.register_route("/warehouse/status", "GET", get_warehouse_status) VWED.function.register("allocate_storage", allocate_storage_func) # ่„šๆœฌB: robot/dispatcher.py def boot(): VWED.api.register_route("/robot/dispatch", "POST", dispatch_robot) VWED.function.register("get_robot_status", get_robot_status_func) # ไธคไธช่„šๆœฌๅฏไปฅๅŒๆ—ถ่ฟ่กŒ๏ผŒๆไพ›ไธๅŒ็š„ๆœๅŠก # A่„šๆœฌ: script_id = "warehouse/controller.py_1640000000000" # B่„šๆœฌ: script_id = "robot/dispatcher.py_1640000001000" ``` ### 10.2 ่„šๆœฌๆœๅŠก็ฎก็†ๆ“ไฝœ ```python # ๅฏๅŠจไป“ๅบ“ๆŽงๅˆถ่„šๆœฌ POST /script-engine/start { "script_path": "warehouse/controller.py" } # ๅฏๅŠจๆœบๅ™จไบบ่ฐƒๅบฆ่„šๆœฌ POST /script-engine/start { "script_path": "robot/dispatcher.py" } # ๆŸฅ็œ‹่ฟ่กŒไธญ็š„่„šๆœฌ GET /script-engine/running # ่ฟ”ๅ›ž: [ { "script_id": "warehouse/controller.py_1640000000000", "script_path": "warehouse/controller.py", "started_at": "2024-01-01T10:00:00", "status": "running", "registrations": {"apis": 1, "functions": 1, "events": 0, "timers": 0} }, { "script_id": "robot/dispatcher.py_1640000001000", "script_path": "robot/dispatcher.py", "started_at": "2024-01-01T10:01:00", "status": "running", "registrations": {"apis": 1, "functions": 1, "events": 0, "timers": 0} } ] # ๅœๆญขไป“ๅบ“ๆŽงๅˆถ่„šๆœฌ๏ผˆๆœบๅ™จไบบ่„šๆœฌ็ปง็ปญ่ฟ่กŒ๏ผ‰ POST /script-engine/stop { "script_id": "warehouse/controller.py_1640000000000" } ``` --- ## ๅไธ€ใ€ๆก†ๆžถๆ‰ฉๅฑ•ๆŒ‡ๅ— ### 11.1 ๆ–ฐๅขžๅ†…็ฝฎๆจกๅ—ๆ‰ฉๅฑ•ๆญฅ้ชค #### ๆญฅ้ชค1๏ผšๅˆ›ๅปบๆจกๅ—ๆŽฅๅฃๅฎšไน‰ ```python # scripts/modules/ai_module.py from abc import ABC, abstractmethod class BaseVWEDAIModule(ABC): """AIๆจกๅ—ๅŸบ็ก€ๆŽฅๅฃ""" @abstractmethod def predict(self, data, model_name: str = "default"): """้ข„ๆต‹ๆŽฅๅฃ""" pass @abstractmethod def train(self, training_data, model_config: dict): """่ฎญ็ปƒๆŽฅๅฃ""" pass @abstractmethod def get_available_models(self): """่Žทๅ–ๅฏ็”จๆจกๅž‹ๅˆ—่กจ""" pass class VWEDAIModule(BaseVWEDAIModule): """AIๆจกๅ—ๅฎž็Žฐ""" def __init__(self, registry): self.registry = registry self.models = {} def predict(self, data, model_name: str = "default"): """ๆ‰ง่กŒ้ข„ๆต‹""" if model_name not in self.models: raise ValueError(f"ๆจกๅž‹ {model_name} ๆœชๆ‰พๅˆฐ") # ๅฎž็Žฐ้ข„ๆต‹้€ป่พ‘ return {"prediction": "result", "confidence": 0.95} def train(self, training_data, model_config: dict): """่ฎญ็ปƒๆจกๅž‹""" model_name = model_config.get("name", "trained_model") # ๅฎž็Žฐ่ฎญ็ปƒ้€ป่พ‘ self.models[model_name] = {"status": "trained", "accuracy": 0.98} return {"success": True, "model_name": model_name} def get_available_models(self): """่Žทๅ–ๅฏ็”จๆจกๅž‹""" return list(self.models.keys()) ``` #### ๆญฅ้ชค2๏ผšๆณจๅ†ŒๅˆฐVWEDๅฏน่ฑกๅทฅๅŽ‚ ```python # scripts/services/script_builtin_functions.py class VWEDObjectFactory: """VWEDๅฏน่ฑกๅทฅๅŽ‚ - ๆ”ฏๆŒๅŠจๆ€ๆจกๅ—ๆ‰ฉๅฑ•""" def __init__(self): self._modules = {} self._load_core_modules() self._load_extended_modules() # ๅŠ ่ฝฝๆ‰ฉๅฑ•ๆจกๅ— def _load_extended_modules(self): """ๅŠ ่ฝฝๆ‰ฉๅฑ•ๆจกๅ—""" # AIๆจกๅ— from scripts.modules.ai_module import VWEDAIModule self._modules['ai'] = VWEDAIModule(self.registry) # ่ง†่ง‰ๆจกๅ— from scripts.modules.vision_module import VWEDVisionModule self._modules['vision'] = VWEDVisionModule(self.registry) # ็ฎ—ๆณ•ๆจกๅ— from scripts.modules.algorithm_module import VWEDAlgorithmModule self._modules['algorithm'] = VWEDAlgorithmModule(self.registry) def register_custom_module(self, module_name: str, module_instance): """ๆณจๅ†Œ่‡ชๅฎšไน‰ๆจกๅ—""" self._modules[module_name] = module_instance def __getattr__(self, name): if name in self._modules: return self._modules[name] raise AttributeError(f"'VWED' object has no attribute '{name}'") ``` #### ๆญฅ้ชค3๏ผš่„šๆœฌไธญไฝฟ็”จๆ–ฐๆจกๅ— ```python # ่„šๆœฌ็คบไพ‹๏ผšai_processor.py def boot(): """AIๅค„็†่„šๆœฌๅ…ฅๅฃ""" VWED.log.info("ai", "AIๅค„็†่„šๆœฌๅฏๅŠจ") # ๆณจๅ†ŒAI้ข„ๆต‹ๆŽฅๅฃ VWED.api.register_route("/ai/predict", "POST", ai_predict_handler) # ๆณจๅ†ŒAI่ฎญ็ปƒๆŽฅๅฃ VWED.api.register_route("/ai/train", "POST", ai_train_handler) # ๆณจๅ†Œๆจกๅž‹็ฎก็†ๅ‡ฝๆ•ฐ VWED.function.register("get_model_info", get_model_info_func) def ai_predict_handler(request): """AI้ข„ๆต‹ๅค„็†ๅ™จ""" data = request.get("json", {}) model_name = data.get("model", "default") input_data = data.get("data") # ไฝฟ็”จVWED.aiๆจกๅ— result = VWED.ai.predict(input_data, model_name) return {"success": True, "result": result} def ai_train_handler(request): """AI่ฎญ็ปƒๅค„็†ๅ™จ""" data = request.get("json", {}) training_data = data.get("training_data") model_config = data.get("config", {}) # ไฝฟ็”จVWED.aiๆจกๅ— result = VWED.ai.train(training_data, model_config) return {"success": True, "result": result} def get_model_info_func(args): """่Žทๅ–ๆจกๅž‹ไฟกๆฏ""" models = VWED.ai.get_available_models() return {"available_models": models} ``` ### 11.2 ๅ่ฎฎๆ‰ฉๅฑ•ๆŒ‡ๅ— #### ๆ‰ฉๅฑ•gRPCๅ่ฎฎๆ”ฏๆŒ ```python # scripts/interfaces/grpc_interface.py import grpc from concurrent import futures class ScriptGRPCAPI: """่„šๆœฌgRPCๆŽฅๅฃ""" def __init__(self, registry): self.registry = registry self.server = None def register_grpc_service(self, service_name: str, service_impl, port: int = 50051): """ๆณจๅ†ŒgRPCๆœๅŠก""" script_id = self.registry.current_script_id if not self.server: self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) # ๆณจๅ†ŒๆœๅŠกๅˆฐgRPCๆœๅŠกๅ™จ # add_ServicenameServicer_to_server(service_impl, self.server) # ่ฎฐๅฝ•ๅˆฐๆณจๅ†Œไธญๅฟƒ self.registry.grpc_services[f"{service_name}:{port}"] = { "service_impl": service_impl, "port": port, "script_id": script_id, "registered_at": datetime.now().isoformat() } self.server.add_insecure_port(f'[::]:{port}') self.server.start() return {"success": True, "service": service_name, "port": port} ``` ### 11.3 ๅฎ‰ๅ…จ็ญ–็•ฅๆ‰ฉๅฑ• #### ไผไธš็บงๅฎ‰ๅ…จๆ‰ฉๅฑ• ```python # scripts/engine/advanced_security.py class EnterpriseSecuritySandbox(ScriptSecuritySandbox): """ไผไธš็บงๅฎ‰ๅ…จๆฒ™็ฎฑ""" def __init__(self): super().__init__() self.enterprise_policies = { "ip_whitelist": [], "domain_blacklist": [], "api_rate_limit": 100, # ๆฏๅˆ†้’Ÿ่ฏทๆฑ‚ๆ•ฐ "resource_quota": { "max_memory": "512MB", "max_cpu_percent": 30, "max_execution_time": 300 # ็ง’ } } def validate_network_access(self, target_host: str, target_port: int) -> bool: """้ชŒ่ฏ็ฝ‘็ปœ่ฎฟ้—ฎๆƒ้™""" # ๆฃ€ๆŸฅIP็™ฝๅๅ• if self.enterprise_policies["ip_whitelist"]: if target_host not in self.enterprise_policies["ip_whitelist"]: return False # ๆฃ€ๆŸฅๅŸŸๅ้ป‘ๅๅ• for blocked_domain in self.enterprise_policies["domain_blacklist"]: if blocked_domain in target_host: return False return super().validate_network_access(target_host, target_port) def check_api_rate_limit(self, script_id: str) -> bool: """ๆฃ€ๆŸฅAPI่ฐƒ็”จ้ข‘็އ้™ๅˆถ""" # ๅฎž็Žฐ้ข‘็އ้™ๅˆถ้€ป่พ‘ return True def monitor_resource_usage(self, script_id: str) -> dict: """็›‘ๆŽง่ต„ๆบไฝฟ็”จๆƒ…ๅ†ต""" # ๅฎž็Žฐ่ต„ๆบ็›‘ๆŽง้€ป่พ‘ return { "memory_usage": "128MB", "cpu_usage": "15%", "execution_time": 120 } ``` ### 11.4 ๆ—ฅๅฟ—็›ฎๆ ‡ๆ‰ฉๅฑ• #### ๅคš็›ฎๆ ‡ๆ—ฅๅฟ—็ณป็ปŸ ```python # scripts/services/advanced_logger.py class MultiTargetLogger(ScriptWebSocketLoggerService): """ๅคš็›ฎๆ ‡ๆ—ฅๅฟ—ๆœๅŠก""" def __init__(self): super().__init__() self.log_targets = { "file": FileLogTarget(), "database": DatabaseLogTarget(), "elasticsearch": ElasticsearchLogTarget(), "webhook": WebhookLogTarget() } def log(self, level: str, tag: str, message: str, script_id: str = None): """ๅคš็›ฎๆ ‡ๆ—ฅๅฟ—่พ“ๅ‡บ""" log_entry = { "timestamp": datetime.now().isoformat(), "level": level, "tag": tag, "message": message, "script_id": script_id } # ๅ‘้€ๅˆฐๆ‰€ๆœ‰ๅฏ็”จ็š„ๆ—ฅๅฟ—็›ฎๆ ‡ for target_name, target in self.log_targets.items(): if target.is_enabled(): target.write_log(log_entry) # ็ปง็ปญๅŽŸๆœ‰็š„WebSocketๆŽจ้€ super().log(level, tag, message, script_id) class DatabaseLogTarget: """ๆ•ฐๆฎๅบ“ๆ—ฅๅฟ—็›ฎๆ ‡""" def write_log(self, log_entry: dict): # ๅ†™ๅ…ฅๆ•ฐๆฎๅบ“ pass def is_enabled(self) -> bool: return True class ElasticsearchLogTarget: """Elasticsearchๆ—ฅๅฟ—็›ฎๆ ‡""" def write_log(self, log_entry: dict): # ๅ†™ๅ…ฅElasticsearch pass def is_enabled(self) -> bool: return False # ๅฏ้…็ฝฎ ``` --- ## ๅไบŒใ€ๅฝ“ๅ‰ๅฎž็Žฐ็Šถๆ€ไธŽๆŠ€ๆœฏ็ป†่Š‚ ### 12.1 script.py ๅค„็†ๅ™จๅฎž็Žฐๅˆ†ๆž ๅŸบไบŽ `services/execution/handlers/script.py` ็š„ๅฝ“ๅ‰ๅฎž็Žฐ๏ผŒ่„šๆœฌๅผ•ๆ“Žๅ…ทๅค‡ไปฅไธ‹ๆŠ€ๆœฏ็‰นๆ€ง๏ผš #### 12.1.1 ๅŒๆจกๅผๅค„็†ๅ™จ่ฎพ่ฎก ```python # ไธค็ง่„šๆœฌๅค„็†ๆจกๅผ @register_handler(ScriptBlockName.SET_TASK_VARIABLES) # ๅ˜้‡่ฎพ็ฝฎๆจกๅผ @register_handler(ScriptBlockName.SCRIPT) # ่„šๆœฌๆ‰ง่กŒๆจกๅผ ``` #### 12.1.2 ๅŠจๆ€ๆจกๅ—ๅŠ ่ฝฝๆœบๅˆถ ```python # ๅ›บๅฎš่„šๆœฌ่ทฏๅพ„๏ผˆๅฝ“ๅ‰ๅฎž็Žฐ๏ผ‰ script_file = "scripts/user_save/test1.py" # ๅŠจๆ€ๅŠ ่ฝฝๅฎž็Žฐ spec = importlib.util.spec_from_file_location("user_script", script_file) module = importlib.util.module_from_spec(spec) sys.modules["user_script"] = module spec.loader.exec_module(module) ``` #### 12.1.3 ๆ™บ่ƒฝๅ‚ๆ•ฐไผ ้€’็ฎ—ๆณ• ```python # ๅ‚ๆ•ฐ็ฑปๅž‹ๆ™บ่ƒฝ่ฏ†ๅˆซ if isinstance(function_args, dict): kwargs = function_args elif isinstance(function_args, list): args = function_args elif isinstance(function_args, str): function_args = eval(function_args) # ๅ‡ฝๆ•ฐ็ญพๅๆฃ€ๆต‹ sig = inspect.signature(func) params = list(sig.parameters.keys()) if len(params) == 1 and isinstance(function_args, dict) and not args: result_value = func(function_args) # ๅ•ๅ‚ๆ•ฐๅญ—ๅ…ธไผ ้€’ else: result_value = func(*args, **kwargs) # ๆ ‡ๅ‡†ๅ‚ๆ•ฐไผ ้€’ ``` #### 12.1.4 ๅผ‚ๆญฅๆ‰ง่กŒๆฃ€ๆต‹ๆœบๅˆถ ```python # ๅ็จ‹ๆฃ€ๆต‹ๅ’Œ็ญ‰ๅพ… if inspect.iscoroutine(result_value): import asyncio result_value = await result_value ``` #### 12.1.5 ๅ˜้‡ไธŠไธ‹ๆ–‡็ฎก็† ```python # SET_TASK_VARIABLES ๆจกๅผ๏ผšๅคšๅ˜้‡่ฎพ็ฝฎ if isinstance(result_value, dict): for key, value in result_value.items(): context.set_variable(key, value) else: context.set_variable("scriptResult", result_value) # SCRIPT ๆจกๅผ๏ผšๅ•ไธ€็ป“ๆžœๅ˜้‡ context.set_variable("scriptResult", result_value) ``` #### 12.1.6 ๆ•ฐๆฎๅบ“ๆŒไน…ๅŒ–ๆœบๅˆถ ```python # ๅ˜้‡ไฟๅญ˜ๅˆฐๆ•ฐๆฎๅบ“ variables_json = json.dumps(context.variables, ensure_ascii=False) stmt = update(VWEDTaskRecord).where(VWEDTaskRecord.id == task_record_id).values( variables=variables_json ) ``` ### 12.2 ๅฝ“ๅ‰ๆžถๆž„็š„ไผ˜ๅŠฟไธŽๅฑ€้™ #### ไผ˜ๅŠฟ๏ผš 1. **็ฎ€ๅ•้ซ˜ๆ•ˆ**๏ผšๅ›บๅฎš่„šๆœฌ่ทฏๅพ„๏ผŒๅ‡ๅฐ‘้…็ฝฎๅคๆ‚ๅบฆ 2. **็ฑปๅž‹ๆ™บ่ƒฝ**๏ผš่‡ชๅŠจ่ฏ†ๅˆซๅ‚ๆ•ฐ็ฑปๅž‹ๅ’Œไผ ้€’ๆ–นๅผ 3. **ๅผ‚ๆญฅๆ”ฏๆŒ**๏ผšๅฎŒๆ•ด็š„ๅ็จ‹ๆฃ€ๆต‹ๅ’Œๆ‰ง่กŒๆœบๅˆถ 4. **ๅŒๆจกๅผ่ฎพ่ฎก**๏ผš็ตๆดป็š„ๅ˜้‡่ฎพ็ฝฎๅ’Œ่„šๆœฌๆ‰ง่กŒๆจกๅผ 5. **ๆทฑๅบฆ้›†ๆˆ**๏ผšไธŽ VWED ไปปๅŠก็ณป็ปŸๆ— ็ผ้›†ๆˆ #### ๅฝ“ๅ‰ๅฑ€้™๏ผš 1. **ๅ›บๅฎš่ทฏๅพ„**๏ผš่„šๆœฌ่ทฏๅพ„็กฌ็ผ–็ ไธบ `scripts/user_save/test1.py` 2. **ๅ•ๆ–‡ไปถ้™ๅˆถ**๏ผšๆ— ๆณ•ๅŠจๆ€้€‰ๆ‹ฉไธๅŒ็š„่„šๆœฌๆ–‡ไปถ 3. **็ผบๅฐ‘ๆ–‡ไปถ็ฎก็†**๏ผšๆฒกๆœ‰่„šๆœฌๆ–‡ไปถ็š„ๅˆ›ๅปบใ€็ผ–่พ‘ใ€ๅˆ ้™คๅŠŸ่ƒฝ 4. **ๆ— ๆ—ฅๅฟ—ๆŽจ้€**๏ผš็ผบๅฐ‘ WebSocket ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€ๅŠŸ่ƒฝ 5. **ๆ— ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†**๏ผš็ผบๅฐ‘่„šๆœฌๅฏๅŠจใ€ๅœๆญขใ€็Šถๆ€็›‘ๆŽง ### 12.3 ๆ‰ฉๅฑ•ๆ”น่ฟ›ๅปบ่ฎฎ #### 12.3.1 ๅŠจๆ€่„šๆœฌ่ทฏๅพ„ๆ”ฏๆŒ ```python # ๅปบ่ฎฎๆ”น่ฟ›๏ผšๆ”ฏๆŒๅŠจๆ€่„šๆœฌ่ทฏๅพ„ script_file = input_params.get("scriptPath", "scripts/user_save/test1.py") ``` #### 12.3.2 ๆ–‡ไปถ็ฎก็†ๅŠŸ่ƒฝๆ‰ฉๅฑ• - ๆทปๅŠ ่„šๆœฌๆ–‡ไปถ CRUD ๆ“ไฝœ API - ๆ”ฏๆŒ็›ฎๅฝ•็ป“ๆž„็ฎก็† - ๅฎž็Žฐๅœจ็บฟ็ผ–่พ‘ๅ™จ้›†ๆˆ #### 12.3.3 ๅฎžๆ—ถๆ—ฅๅฟ—ๆŽจ้€้›†ๆˆ - ้›†ๆˆ WebSocket ๆ—ฅๅฟ—ๆŽจ้€ๆœๅŠก - ่„šๆœฌๆ‰ง่กŒ่ฟ‡็จ‹็š„ๅฎžๆ—ถ็›‘ๆŽง - ๆ‰ง่กŒ็Šถๆ€ๅ’Œ็ป“ๆžœ็š„ๅฎžๆ—ถๅ้ฆˆ #### 12.3.4 ็”Ÿๅ‘ฝๅ‘จๆœŸ็ฎก็†ๆ‰ฉๅฑ• - ่„šๆœฌ่ฟ›็จ‹็š„ๅฏๅŠจใ€ๅœๆญขๆŽงๅˆถ - ่ฟ่กŒ็Šถๆ€็›‘ๆŽงๅ’Œ็ฎก็† - ่ต„ๆบไฝฟ็”จๆƒ…ๅ†ต่ฟฝ่ธช --- ## ๅไธ‰ใ€ๆœ€ไฝณๅฎž่ทตไธŽๆณจๆ„ไบ‹้กน ### 13.1 ่„šๆœฌๅผ€ๅ‘ๆœ€ไฝณๅฎž่ทต #### ๆŽจ่็š„่„šๆœฌ็ป“ๆž„ ```python # ๆ ‡ๅ‡†่„šๆœฌๆจกๆฟ import asyncio from typing import Dict, Any def boot(): """่„šๆœฌๅ…ฅๅฃๅ‡ฝๆ•ฐ - ๅฟ…้กปๅฎž็Žฐ""" VWED.log.info("script", "่„šๆœฌๅฏๅŠจไธญ...") # 1. ๆณจๅ†ŒAPIๆŽฅๅฃ register_apis() # 2. ๆณจๅ†Œ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐ register_functions() # 3. ๆณจๅ†Œไบ‹ไปถ็›‘ๅฌๅ™จ register_events() # 4. ๅฏๅŠจๅฎšๆ—ถไปปๅŠก start_timers() VWED.log.info("script", "่„šๆœฌๅฏๅŠจๅฎŒๆˆ") def register_apis(): """ๆณจๅ†ŒAPIๆŽฅๅฃ""" VWED.api.register_route("/health", "GET", health_check) VWED.api.register_route("/process", "POST", process_data) def register_functions(): """ๆณจๅ†Œ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐ""" VWED.function.register("calculate", calculate_func, "ๆ•ฐๆฎ่ฎก็ฎ—ๅ‡ฝๆ•ฐ") VWED.function.register("validate", validate_func, "ๆ•ฐๆฎ้ชŒ่ฏๅ‡ฝๆ•ฐ") def register_events(): """ๆณจๅ†Œไบ‹ไปถ็›‘ๅฌๅ™จ""" VWED.event.listen("data_received", on_data_received) VWED.event.listen("task_completed", on_task_completed) def start_timers(): """ๅฏๅŠจๅฎšๆ—ถไปปๅŠก""" VWED.timer.schedule(interval=60, handler=periodic_cleanup, repeat=True) # APIๅค„็†ๅ‡ฝๆ•ฐ def health_check(request: Dict[str, Any]) -> Dict[str, Any]: """ๅฅๅบทๆฃ€ๆŸฅๆŽฅๅฃ""" return {"status": "healthy", "timestamp": datetime.now().isoformat()} async def process_data(request: Dict[str, Any]) -> Dict[str, Any]: """ๆ•ฐๆฎๅค„็†ๆŽฅๅฃ๏ผˆๆ”ฏๆŒๅผ‚ๆญฅ๏ผ‰""" data = request.get("json", {}) try: # ๆ•ฐๆฎ้ชŒ่ฏ if not validate_input(data): return {"error": "ๆ•ฐๆฎ้ชŒ่ฏๅคฑ่ดฅ"} # ๆ•ฐๆฎๅค„็† result = await process_business_logic(data) # ่งฆๅ‘ไบ‹ไปถ VWED.event.emit("data_processed", {"result": result}) return {"success": True, "result": result} except Exception as e: VWED.log.error("script", f"ๆ•ฐๆฎๅค„็†ๅคฑ่ดฅ: {str(e)}") return {"error": str(e)} # ่‡ชๅฎšไน‰ๅ‡ฝๆ•ฐ def calculate_func(args: Dict[str, Any]) -> Dict[str, Any]: """่ฎก็ฎ—ๅ‡ฝๆ•ฐ""" # ๅฎž็Žฐ่ฎก็ฎ—้€ป่พ‘ return {"result": "calculated_value"} # ไบ‹ไปถๅค„็†ๅ‡ฝๆ•ฐ def on_data_received(event_data: Dict[str, Any]): """ๆ•ฐๆฎๆŽฅๆ”ถไบ‹ไปถๅค„็†""" VWED.log.info("event", f"ๆ”ถๅˆฐๆ•ฐๆฎ: {event_data}") # ๅฎšๆ—ถไปปๅŠกๅ‡ฝๆ•ฐ def periodic_cleanup(): """ๅฎšๆœŸๆธ…็†ไปปๅŠก""" VWED.log.info("timer", "ๆ‰ง่กŒๅฎšๆœŸๆธ…็†") ``` ### 13.2 ๆ€ง่ƒฝไผ˜ๅŒ–ๅปบ่ฎฎ 1. **ๅผ‚ๆญฅ็ผ–็จ‹**๏ผšไผ˜ๅ…ˆไฝฟ็”จๅผ‚ๆญฅๅ‡ฝๆ•ฐๅค„็†IOๅฏ†้›†ๅž‹ๆ“ไฝœ 2. **่ต„ๆบ็ฎก็†**๏ผšๅŠๆ—ถ้‡Šๆ”พไธ้œ€่ฆ็š„่ต„ๆบ๏ผŒ้ฟๅ…ๅ†…ๅญ˜ๆณ„ๆผ 3. **็ผ“ๅญ˜็ญ–็•ฅ**๏ผšๅˆ็†ไฝฟ็”จ็ผ“ๅญ˜ๅ‡ๅฐ‘้‡ๅค่ฎก็ฎ— 4. **ๆ‰น้‡ๅค„็†**๏ผšๅฏนไบŽๅคง้‡ๆ•ฐๆฎๅค„็†๏ผŒ้‡‡็”จๆ‰น้‡ๅค„็†ๆ–นๅผ 5. **่ฟžๆŽฅๆฑ **๏ผšไฝฟ็”จ่ฟžๆŽฅๆฑ ็ฎก็†ๆ•ฐๆฎๅบ“ๅ’Œๅค–้ƒจๆœๅŠก่ฟžๆŽฅ ### 13.3 ๅฎ‰ๅ…จๆณจๆ„ไบ‹้กน 1. **่พ“ๅ…ฅ้ชŒ่ฏ**๏ผšไธฅๆ ผ้ชŒ่ฏๆ‰€ๆœ‰ๅค–้ƒจ่พ“ๅ…ฅ 2. **ๆƒ้™ๆŽงๅˆถ**๏ผšๅฎžๆ–ฝๆœ€ๅฐๆƒ้™ๅŽŸๅˆ™ 3. **ๆ•ๆ„Ÿๆ•ฐๆฎ**๏ผšไธๅœจ่„šๆœฌไธญ็กฌ็ผ–็ ๆ•ๆ„Ÿไฟกๆฏ 4. **็ฝ‘็ปœ่ฎฟ้—ฎ**๏ผšๅช่ฎฟ้—ฎๅฟ…่ฆ็š„ๅค–้ƒจ่ต„ๆบ 5. **ๆ—ฅๅฟ—ๅฎ‰ๅ…จ**๏ผš้ฟๅ…ๅœจๆ—ฅๅฟ—ไธญ่ฎฐๅฝ•ๆ•ๆ„Ÿไฟกๆฏ ### 13.4 ๆ•…้šœๆŽ’ๆŸฅๆŒ‡ๅ— #### ๅธธ่ง้—ฎ้ข˜ๅŠ่งฃๅ†ณๆ–นๆกˆ | ้—ฎ้ข˜็ฑปๅž‹ | ็—‡็Šถ | ๅฏ่ƒฝๅŽŸๅ›  | ่งฃๅ†ณๆ–นๆกˆ | |---------|------|----------|----------| | ่„šๆœฌๅฏๅŠจๅคฑ่ดฅ | boot()ๅ‡ฝๆ•ฐๆŠฅ้”™ | ่ฏญๆณ•้”™่ฏฏใ€ไพ่ต–็ผบๅคฑ | ๆฃ€ๆŸฅ่„šๆœฌ่ฏญๆณ•๏ผŒ็กฎ่ฎคไพ่ต–ๅฎ‰่ฃ… | | APIๆ— ๆณ•่ฎฟ้—ฎ | 404้”™่ฏฏ | ่ทฏ็”ฑๆœชๆณจๅ†Œใ€่„šๆœฌๅทฒๅœๆญข | ๆฃ€ๆŸฅ่„šๆœฌ่ฟ่กŒ็Šถๆ€ๅ’ŒAPIๆณจๅ†Œ | | ๅ†…ๅญ˜ๆณ„ๆผ | ๅ†…ๅญ˜ไฝฟ็”จๆŒ็ปญๅขž้•ฟ | ๅพช็Žฏๅผ•็”จใ€่ต„ๆบๆœช้‡Šๆ”พ | ไฝฟ็”จๅ†…ๅญ˜ๅˆ†ๆžๅทฅๅ…ท๏ผŒๆฃ€ๆŸฅ่ต„ๆบ้‡Šๆ”พ | | ๆ€ง่ƒฝไธ‹้™ | ๅ“ๅบ”ๆ—ถ้—ดๅ˜้•ฟ | ้˜ปๅกžๆ“ไฝœใ€่ต„ๆบ็ซžไบ‰ | ไผ˜ๅŒ–็ฎ—ๆณ•๏ผŒไฝฟ็”จๅผ‚ๆญฅ็ผ–็จ‹ | | ๅนถๅ‘้—ฎ้ข˜ | ๆ•ฐๆฎไธไธ€่‡ด | ๅ…ฑไบซ็Šถๆ€็ซžไบ‰ | ไฝฟ็”จ้”ๆœบๅˆถๆˆ–ๆถˆๆฏ้˜Ÿๅˆ— | --- ## ๅๅ››ใ€ๆ€ป็ป“ ๆœฌๆ–‡ๆกฃ่ฏฆ็ป†ๆ่ฟฐไบ†VWED้ซ˜ๆ‰ฉๅฑ•ๆ€งPython่„šๆœฌๆ‰ง่กŒๅผ•ๆ“Ž็š„ๅฎŒๆ•ดๆžถๆž„่ฎพ่ฎกใ€‚่ฏฅๆก†ๆžถๅ…ทๆœ‰ไปฅไธ‹ๆ ธๅฟƒไผ˜ๅŠฟ๏ผš ### 14.1 ๆ ธๅฟƒไผ˜ๅŠฟ 1. **้ซ˜ๅบฆๅฏๆ‰ฉๅฑ•**๏ผšๆจกๅ—ๅŒ–่ฎพ่ฎก๏ผŒๆ”ฏๆŒไปปๆ„ๅŠŸ่ƒฝๆจกๅ—ๆ‰ฉๅฑ• 2. **ๅคš่„šๆœฌๅนถๅ‘**๏ผš็‹ฌ็ซ‹็š„่„šๆœฌID้š”็ฆปๆœบๅˆถ๏ผŒๆ”ฏๆŒๅคš่„šๆœฌๅŒๆ—ถ่ฟ่กŒ 3. **็ปŸไธ€็ฎก็†**๏ผšๅ…จๅฑ€ๅ…ฑไบซ่„šๆœฌ๏ผŒ็ฎ€ๅŒ–็ฎก็†ๅคๆ‚ๅบฆ 4. **ไผไธš็บงๅฎ‰ๅ…จ**๏ผšๅคšๅฑ‚ๅฎ‰ๅ…จ้˜ฒๆŠค๏ผŒๆปก่ถณไผไธšๅฎ‰ๅ…จ่ฆๆฑ‚ 5. **ไบ‘ๅŽŸ็”Ÿๆ”ฏๆŒ**๏ผšDockerๅŒ–้ƒจ็ฝฒ๏ผŒๆ”ฏๆŒKubernetes้›†็พค 6. **ๅฎŒๅ–„็›‘ๆŽง**๏ผšๅ…จๆ–นไฝ็š„็›‘ๆŽงๆŒ‡ๆ ‡ๅ’Œๅ‘Š่ญฆๆœบๅˆถ ### 14.2 ๆ‰ฉๅฑ•่ƒฝๅŠ› - **ๅ†…็ฝฎๆจกๅ—ๆ‰ฉๅฑ•**๏ผšAIใ€่ง†่ง‰ใ€็ฎ—ๆณ•็ญ‰ไธ“ไธšๆจกๅ— - **ๅ่ฎฎๆ‰ฉๅฑ•**๏ผšgRPCใ€MQTT็ญ‰ๅคš็ง้€šไฟกๅ่ฎฎ - **ๅฎ‰ๅ…จ็ญ–็•ฅๆ‰ฉๅฑ•**๏ผšไผไธš็บงๅฎ‰ๅ…จๆŽงๅˆถ็ญ–็•ฅ - **ๆ—ฅๅฟ—็›ฎๆ ‡ๆ‰ฉๅฑ•**๏ผšๅคš็งๆ—ฅๅฟ—ๅญ˜ๅ‚จๅ’Œๅˆ†ๆžๆ–นๆกˆ - **็›‘ๆŽงๆ‰ฉๅฑ•**๏ผšPrometheusใ€Grafana็ญ‰็›‘ๆŽง้›†ๆˆ ### 14.3 ๅบ”็”จๅœบๆ™ฏ - ๅทฅไธš่‡ชๅŠจๅŒ–ๆŽงๅˆถ็ณป็ปŸ - ๆ™บ่ƒฝไป“ๅ‚จ็ฎก็†็ณป็ปŸ - ๆœบๅ™จไบบ่ฐƒๅบฆๆŽงๅˆถ็ณป็ปŸ - IoT่ฎพๅค‡็ฎก็†ๅนณๅฐ - ๆ•ฐๆฎๅค„็†ไธŽๅˆ†ๆžๅนณๅฐ ่ฏฅๆก†ๆžถไธบไผไธšๆไพ›ไบ†ไธ€ไธชๅผบๅคงใ€็ตๆดปใ€ๅฎ‰ๅ…จ็š„่„šๆœฌๆ‰ง่กŒ็Žฏๅขƒ๏ผŒ่ƒฝๅคŸๆปก่ถณๅคๆ‚ไธšๅŠกๅœบๆ™ฏ็š„้œ€ๆฑ‚๏ผŒๅนถๅ…ทๅค‡่‰ฏๅฅฝ็š„ๆ‰ฉๅฑ•ๆ€งๅ’Œ็ปดๆŠคๆ€งใ€‚