Files
cad-data-router/sop.md
2026-03-05 14:45:06 +01:00

397 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SOP CAD File Router MVP
## Obiettivo
Realizzare un'app desktop (.exe) che:
- accetti una cartella o uno ZIP
- legga file CAD (.prt .asm .drw)
- analizzi nome file ed estensione
- applichi regole di routing
- copi i file verso destinazioni configurate (anche share di rete)
Tecnologie:
- Node.js
- Electron
- unzipper
- fs-extra
---
## 1. Prerequisiti
Installare:
- Node.js LTS
- Git
- Visual Studio Code / VSCodium
Verifica:
```
node -v
npm -v
```
---
## 2. Creazione progetto
```
mkdir cad-file-router
cd cad-file-router
npm init -y
```
Installare dipendenze:
```
npm install electron unzipper fs-extra
npm install electron-builder --save-dev
```
---
## 3. Struttura progetto
```
cad-file-router
├── package.json
├── main.js
├── preload.js
├── renderer
│ ├── index.html
│ └── renderer.js
├── services
│ ├── router.js
│ ├── zipProcessor.js
│ ├── folderProcessor.js
│ └── configService.js
└── config
└── defaultConfig.json
```
---
## 4. Configurazione iniziale
`config/defaultConfig.json`
```
{
"rules": [
{
"ext": "prt",
"pattern": "^1",
"destination": "\\\\SERVER-CAD\\clienti1\\PRT"
},
{
"ext": "prt",
"pattern": "^6",
"destination": "\\\\SERVER-CAD\\clienti2\\PRT"
},
{
"ext": "asm",
"destination": "\\\\SERVER-CAD\\assembly"
},
{
"ext": "drw",
"destination": "\\\\SERVER-CAD\\drawing"
}
]
}
```
---
## 5. Config Service
`services/configService.js`
```
const fs = require("fs-extra")
const path = require("path")
const os = require("os")
const configPath = path.join(os.homedir(), ".cad-router-config.json")
function loadConfig(){
if(fs.existsSync(configPath)){
return fs.readJsonSync(configPath)
}
const defaultConfig = fs.readJsonSync(
path.join(__dirname,"../config/defaultConfig.json")
)
fs.writeJsonSync(configPath, defaultConfig)
return defaultConfig
}
module.exports = { loadConfig }
```
---
## 6. Motore di routing
`services/router.js`
```
const path = require("path")
function findDestination(filename, config){
const ext = path.extname(filename).slice(1)
for(const rule of config.rules){
if(rule.ext !== ext)
continue
if(!rule.pattern)
return rule.destination
const regex = new RegExp(rule.pattern)
if(regex.test(filename))
return rule.destination
}
return null
}
module.exports = { findDestination }
```
---
## 7. Processare una cartella
`services/folderProcessor.js`
```
const fs = require("fs-extra")
const path = require("path")
const { findDestination } = require("./router")
async function processFolder(folder, config){
const files = await fs.readdir(folder)
for(const file of files){
const src = path.join(folder,file)
const destDir = findDestination(file, config)
if(!destDir) continue
const dest = path.join(destDir,file)
await fs.copy(src,dest)
}
}
module.exports = { processFolder }
```
---
## 8. Processare ZIP
`services/zipProcessor.js`
```
const unzipper = require("unzipper")
const fs = require("fs-extra")
const path = require("path")
const { findDestination } = require("./router")
async function processZip(zipPath, config){
const stream = fs.createReadStream(zipPath)
.pipe(unzipper.Parse())
for await (const entry of stream){
const file = entry.path
const destDir = findDestination(file, config)
if(!destDir){
entry.autodrain()
continue
}
const dest = path.join(destDir,path.basename(file))
entry.pipe(fs.createWriteStream(dest))
}
}
module.exports = { processZip }
```
---
## 9. Main Electron
`main.js`
```
const { app, BrowserWindow, dialog, ipcMain } = require("electron")
const path = require("path")
const { processFolder } = require("./services/folderProcessor")
const { processZip } = require("./services/zipProcessor")
const { loadConfig } = require("./services/configService")
let config = loadConfig()
function createWindow(){
const win = new BrowserWindow({
width:800,
height:600,
webPreferences:{
preload: path.join(__dirname,"preload.js")
}
})
win.loadFile("renderer/index.html")
}
app.whenReady().then(createWindow)
ipcMain.handle("select-folder", async () => {
const result = await dialog.showOpenDialog({
properties:["openDirectory"]
})
if(result.canceled) return
await processFolder(result.filePaths[0], config)
})
ipcMain.handle("select-zip", async () => {
const result = await dialog.showOpenDialog({
filters:[{ name:"Zip", extensions:["zip"] }]
})
if(result.canceled) return
await processZip(result.filePaths[0], config)
})
```
---
## 10. Preload
`preload.js`
```
const { contextBridge, ipcRenderer } = require("electron")
contextBridge.exposeInMainWorld("api", {
selectFolder: () => ipcRenderer.invoke("select-folder"),
selectZip: () => ipcRenderer.invoke("select-zip")
})
```
---
## 11. UI minima
`renderer/index.html`
```
<h2>CAD File Router</h2>
<button onclick="window.api.selectFolder()">
Process Folder
</button>
<button onclick="window.api.selectZip()">
Process ZIP
</button>
```
---
## 12. Avvio applicazione
Nel package.json aggiungere:
```
"scripts": {
"start": "electron ."
}
```
Avviare:
```
npm start
```
---
## 13. Build .exe
Aggiungere nel package.json:
```
"build": {
"appId": "com.cad.router",
"win": {
"target": "nsis"
}
}
```
Build:
```
npx electron-builder
```
Output:
```
dist/
cad-file-router Setup.exe
```
---
## MVP completato quando
- l'app si avvia
- si seleziona ZIP o cartella
- i file .prt .asm .drw vengono analizzati
- i file vengono copiati nelle destinazioni configurate
---
## Migliorie future
- progress bar
- drag and drop ZIP
- modifica regole da UI
- logging operazioni
- parallelismo file
- watch folder automatico