tangled
alpha
login
or
join now
atscan.net
/
atscand
1
fork
atom
wip
1
fork
atom
overview
issues
pulls
pipelines
update
tree.fail
4 months ago
8b930bc5
d131ad8b
+160
-1
2 changed files
expand all
collapse all
unified
split
.gitignore
cmd
atscand
main.go
+1
-1
.gitignore
···
6
6
plc_cache\.tmp/*
7
7
plc_bundles*
8
8
config.yaml
9
9
-
atscand
9
9
+
./atscand
+159
cmd/atscand/main.go
···
1
1
+
package main
2
2
+
3
3
+
import (
4
4
+
"context"
5
5
+
"flag"
6
6
+
"fmt"
7
7
+
"os"
8
8
+
"os/signal"
9
9
+
"syscall"
10
10
+
"time"
11
11
+
12
12
+
"github.com/atscan/atscand/internal/api"
13
13
+
"github.com/atscan/atscand/internal/config"
14
14
+
"github.com/atscan/atscand/internal/log"
15
15
+
"github.com/atscan/atscand/internal/pds"
16
16
+
"github.com/atscan/atscand/internal/plc"
17
17
+
"github.com/atscan/atscand/internal/storage"
18
18
+
"github.com/atscan/atscand/internal/worker"
19
19
+
)
20
20
+
21
21
+
const VERSION = "1.0.0"
22
22
+
23
23
+
func main() {
24
24
+
configPath := flag.String("config", "config.yaml", "path to config file")
25
25
+
verbose := flag.Bool("verbose", false, "enable verbose logging")
26
26
+
flag.Parse()
27
27
+
28
28
+
// Load configuration
29
29
+
cfg, err := config.Load(*configPath)
30
30
+
if err != nil {
31
31
+
fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
32
32
+
os.Exit(1)
33
33
+
}
34
34
+
35
35
+
// Override verbose setting if flag is provided
36
36
+
if *verbose {
37
37
+
cfg.API.Verbose = true
38
38
+
}
39
39
+
40
40
+
// Initialize logger
41
41
+
log.Init(cfg.API.Verbose)
42
42
+
43
43
+
// Print banner
44
44
+
log.Banner(VERSION)
45
45
+
46
46
+
// Print configuration summary
47
47
+
log.PrintConfig(map[string]string{
48
48
+
"Database Type": cfg.Database.Type,
49
49
+
"Database Path": cfg.Database.Path, // Will be auto-redacted
50
50
+
"PLC Directory": cfg.PLC.DirectoryURL,
51
51
+
"PLC Scan Interval": cfg.PLC.ScanInterval.String(),
52
52
+
"PLC Bundle Dir": cfg.PLC.BundleDir,
53
53
+
"PLC Cache": fmt.Sprintf("%v", cfg.PLC.UseCache),
54
54
+
"PLC Index DIDs": fmt.Sprintf("%v", cfg.PLC.IndexDIDs),
55
55
+
"PDS Scan Interval": cfg.PDS.ScanInterval.String(),
56
56
+
"PDS Workers": fmt.Sprintf("%d", cfg.PDS.Workers),
57
57
+
"PDS Timeout": cfg.PDS.Timeout.String(),
58
58
+
"API Host": cfg.API.Host,
59
59
+
"API Port": fmt.Sprintf("%d", cfg.API.Port),
60
60
+
"Verbose Logging": fmt.Sprintf("%v", cfg.API.Verbose),
61
61
+
})
62
62
+
63
63
+
// Initialize database using factory pattern
64
64
+
db, err := storage.NewDatabase(cfg.Database.Type, cfg.Database.Path)
65
65
+
if err != nil {
66
66
+
log.Fatal("Failed to initialize database: %v", err)
67
67
+
}
68
68
+
defer func() {
69
69
+
log.Info("Closing database connection...")
70
70
+
db.Close()
71
71
+
}()
72
72
+
73
73
+
// Set scan retention from config
74
74
+
if cfg.PDS.ScanRetention > 0 {
75
75
+
db.SetScanRetention(cfg.PDS.ScanRetention)
76
76
+
log.Verbose("Scan retention set to %d scans per endpoint", cfg.PDS.ScanRetention)
77
77
+
}
78
78
+
79
79
+
// Run migrations
80
80
+
if err := db.Migrate(); err != nil {
81
81
+
log.Fatal("Failed to run migrations: %v", err)
82
82
+
}
83
83
+
84
84
+
ctx, cancel := context.WithCancel(context.Background())
85
85
+
defer cancel()
86
86
+
87
87
+
// Initialize workers
88
88
+
log.Info("Initializing scanners...")
89
89
+
90
90
+
bundleManager, err := plc.NewBundleManager(cfg.PLC.BundleDir, cfg.PLC.DirectoryURL, db, cfg.PLC.IndexDIDs)
91
91
+
if err != nil {
92
92
+
log.Fatal("Failed to create bundle manager: %v", err)
93
93
+
}
94
94
+
defer bundleManager.Close()
95
95
+
log.Verbose("✓ Bundle manager initialized (shared)")
96
96
+
97
97
+
plcScanner := plc.NewScanner(db, cfg.PLC, bundleManager)
98
98
+
defer plcScanner.Close()
99
99
+
log.Verbose("✓ PLC scanner initialized")
100
100
+
101
101
+
pdsScanner := pds.NewScanner(db, cfg.PDS)
102
102
+
log.Verbose("✓ PDS scanner initialized")
103
103
+
104
104
+
scheduler := worker.NewScheduler()
105
105
+
106
106
+
// Schedule PLC directory scan
107
107
+
scheduler.AddJob("plc_scan", cfg.PLC.ScanInterval, func() {
108
108
+
if err := plcScanner.Scan(ctx); err != nil {
109
109
+
log.Error("PLC scan error: %v", err)
110
110
+
}
111
111
+
})
112
112
+
log.Verbose("✓ PLC scan job scheduled (interval: %s)", cfg.PLC.ScanInterval)
113
113
+
114
114
+
// Schedule PDS availability checks
115
115
+
scheduler.AddJob("pds_scan", cfg.PDS.ScanInterval, func() {
116
116
+
if err := pdsScanner.ScanAll(ctx); err != nil {
117
117
+
log.Error("PDS scan error: %v", err)
118
118
+
}
119
119
+
})
120
120
+
log.Verbose("✓ PDS scan job scheduled (interval: %s)", cfg.PDS.ScanInterval)
121
121
+
122
122
+
// Start API server
123
123
+
log.Info("Starting API server on %s:%d...", cfg.API.Host, cfg.API.Port)
124
124
+
apiServer := api.NewServer(db, cfg.API, cfg.PLC, bundleManager)
125
125
+
go func() {
126
126
+
if err := apiServer.Start(); err != nil {
127
127
+
log.Fatal("API server error: %v", err)
128
128
+
}
129
129
+
}()
130
130
+
131
131
+
// Give the API server a moment to start
132
132
+
time.Sleep(100 * time.Millisecond)
133
133
+
log.Info("✓ API server started successfully")
134
134
+
log.Info("")
135
135
+
log.Info("🚀 ATScanner is running!")
136
136
+
log.Info(" API available at: http://%s:%d", cfg.API.Host, cfg.API.Port)
137
137
+
log.Info(" Press Ctrl+C to stop")
138
138
+
log.Info("")
139
139
+
140
140
+
// Start scheduler
141
141
+
scheduler.Start(ctx)
142
142
+
143
143
+
// Wait for interrupt
144
144
+
sigChan := make(chan os.Signal, 1)
145
145
+
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
146
146
+
<-sigChan
147
147
+
148
148
+
log.Info("")
149
149
+
log.Info("Shutting down gracefully...")
150
150
+
cancel()
151
151
+
152
152
+
log.Info("Stopping API server...")
153
153
+
apiServer.Shutdown(context.Background())
154
154
+
155
155
+
log.Info("Waiting for active tasks to complete...")
156
156
+
time.Sleep(2 * time.Second)
157
157
+
158
158
+
log.Info("✓ Shutdown complete. Goodbye!")
159
159
+
}