Files
cad-data-router/sop.md

397 lines
6.0 KiB
Markdown
Raw Normal View History

2026-03-05 14:45:06 +01:00
# 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