104 lines
3.2 KiB
Go
104 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"aquacontrolai/internal/api"
|
|
collectorengine "aquacontrolai/internal/engine/collector"
|
|
writerengine "aquacontrolai/internal/engine/writer"
|
|
"aquacontrolai/internal/pkg/config"
|
|
"aquacontrolai/internal/protocol"
|
|
"aquacontrolai/internal/protocol/modbus"
|
|
"aquacontrolai/internal/protocol/s7"
|
|
pg "aquacontrolai/internal/repository/postgres"
|
|
td "aquacontrolai/internal/repository/tdengine"
|
|
"aquacontrolai/internal/service/platform"
|
|
"aquacontrolai/migrations"
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
_ "github.com/taosdata/driver-go/v3/taosRestful"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"regexp"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
cfg := config.Load()
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
db, e := pgxpool.New(ctx, cfg.PostgresDSN)
|
|
must(e)
|
|
mustPing(ctx, db)
|
|
_, e = db.Exec(ctx, migrations.PostgreSQL)
|
|
must(e)
|
|
_, e = db.Exec(ctx, migrations.PostgreSQLGroups)
|
|
must(e)
|
|
_, e = db.Exec(ctx, migrations.PostgreSQLWritePoints)
|
|
must(e)
|
|
db.Close()
|
|
pgStore, e := pg.Open(ctx, cfg.PostgresDSN)
|
|
must(e)
|
|
defer pgStore.DB.Close()
|
|
adminDSN := strings.TrimSuffix(cfg.TDengineDSN, "/"+cfg.TDengineDatabase) + "/"
|
|
if !regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]{0,63}$`).MatchString(cfg.TDengineDatabase) {
|
|
must(errors.New("TDENGINE_DATABASE 名称无效"))
|
|
}
|
|
admin, e := sql.Open("taosRestful", adminDSN)
|
|
must(e)
|
|
_, e = admin.ExecContext(ctx, "CREATE DATABASE IF NOT EXISTS `"+cfg.TDengineDatabase+"` KEEP "+fmt.Sprint(cfg.HistoryRetentionDays))
|
|
must(e)
|
|
admin.Close()
|
|
taos, e := td.Open(ctx, cfg.TDengineDSN, cfg.TDengineDatabase)
|
|
must(e)
|
|
defer taos.DB.Close()
|
|
for _, statement := range strings.Split(migrations.TDengine, ";") {
|
|
if strings.TrimSpace(statement) == "" {
|
|
continue
|
|
}
|
|
_, e = taos.DB.ExecContext(ctx, statement)
|
|
must(e)
|
|
}
|
|
registry := protocol.NewRegistry()
|
|
must(registry.Register(s7.Factory{}))
|
|
must(registry.Register(modbus.Factory{}))
|
|
connections := collectorengine.NewManager(pgStore, registry)
|
|
defer connections.Close()
|
|
collector := collectorengine.NewEngine(connections, pgStore, taos, cfg.CollectorWorkers)
|
|
collector.Start(ctx)
|
|
defer collector.Stop()
|
|
writeEngine := &writerengine.Engine{Manager: connections, Store: pgStore}
|
|
svc := &platform.Service{Store: pgStore, Registry: registry, Connections: connections, Writer: writeEngine, Collector: collector, TD: taos}
|
|
router := api.NewRouter(&api.Handler{Platform: svc, History: &platform.History{PG: pgStore, TD: taos, Collector: collector}})
|
|
server := &http.Server{Addr: ":" + cfg.Port, Handler: router, ReadHeaderTimeout: 5 * time.Second}
|
|
go func() {
|
|
slog.Info("AquaControl AI 服务启动", "port", cfg.Port)
|
|
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
slog.Error("HTTP 服务异常", "error", err)
|
|
stop()
|
|
}
|
|
}()
|
|
<-ctx.Done()
|
|
shutdown, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
must(server.Shutdown(shutdown))
|
|
}
|
|
func must(e error) {
|
|
if e != nil {
|
|
slog.Error("应用启动失败", "error", e)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
func mustPing(ctx context.Context, db *pgxpool.Pool) {
|
|
c, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
must(db.Ping(c))
|
|
}
|
|
|
|
var _ *sql.DB
|