diff --git a/db.go b/db.go
index 0652e0a..f1e8f15 100644
--- a/db.go
+++ b/db.go
@@ -48,20 +48,22 @@ func CloseDB() {
}
}
-// 保存传感器数据 - 将浮点值转换为整数存储
-func SaveSensorData(sensorID int, x, y, z float64) error {
+// 保存传感器数据 - 将浮点值转换为整数存储,添加温度支持
+func SaveSensorData(sensorID int, x, y, z, temperature float64) error {
xInt := int(x * SCALING_FACTOR)
yInt := int(y * SCALING_FACTOR)
zInt := int(z * SCALING_FACTOR)
+ tempInt := int(temperature * SCALING_FACTOR)
- query := `INSERT INTO sensor_data (sensor_id, x_value, y_value, z_value) VALUES (?, ?, ?, ?)`
- _, err := db.Exec(query, sensorID, xInt, yInt, zInt)
+ query := `INSERT INTO sensor_data (sensor_id, x_value, y_value, z_value, temperature) VALUES (?, ?, ?, ?, ?)`
+ _, err := db.Exec(query, sensorID, xInt, yInt, zInt, tempInt)
return err
}
-// 获取传感器数据 - 添加时间范围
+// 获取传感器数据 - 添加时间范围,包含温度字段
func GetSensorData(sensorID int, limit int, startDate time.Time, endDate time.Time) ([]SensorData, error) {
query := `SELECT id, sensor_id, x_value, y_value, z_value,
+ COALESCE(temperature, 0) as temperature,
timestamp as timestamp
FROM sensor_data
WHERE sensor_id = ?`
@@ -97,9 +99,9 @@ func GetSensorData(sensorID int, limit int, startDate time.Time, endDate time.Ti
for rows.Next() {
var data SensorData
- var xInt, yInt, zInt int
+ var xInt, yInt, zInt, tempInt int
- err := rows.Scan(&data.ID, &data.SensorID, &xInt, &yInt, &zInt, &data.Timestamp)
+ err := rows.Scan(&data.ID, &data.SensorID, &xInt, &yInt, &zInt, &tempInt, &data.Timestamp)
if err != nil {
return nil, err
}
@@ -107,6 +109,7 @@ func GetSensorData(sensorID int, limit int, startDate time.Time, endDate time.Ti
data.X = float64(xInt) / SCALING_FACTOR
data.Y = float64(yInt) / SCALING_FACTOR
data.Z = float64(zInt) / SCALING_FACTOR
+ data.Temperature = float64(tempInt) / SCALING_FACTOR
result = append(result, data)
}
@@ -114,9 +117,10 @@ func GetSensorData(sensorID int, limit int, startDate time.Time, endDate time.Ti
return result, nil
}
-// 获取所有传感器数据
+// 获取所有传感器数据,包含温度字段
func GetAllSensorData(limit int, startDate time.Time, endDate time.Time) ([]SensorData, error) {
query := `SELECT id, sensor_id, x_value, y_value, z_value,
+ COALESCE(temperature, 0) as temperature,
timestamp as timestamp
FROM sensor_data
WHERE 1=1`
@@ -151,9 +155,9 @@ func GetAllSensorData(limit int, startDate time.Time, endDate time.Time) ([]Sens
for rows.Next() {
var data SensorData
- var xInt, yInt, zInt int
+ var xInt, yInt, zInt, tempInt int
- err := rows.Scan(&data.ID, &data.SensorID, &xInt, &yInt, &zInt, &data.Timestamp)
+ err := rows.Scan(&data.ID, &data.SensorID, &xInt, &yInt, &zInt, &tempInt, &data.Timestamp)
if err != nil {
return nil, err
}
@@ -161,6 +165,7 @@ func GetAllSensorData(limit int, startDate time.Time, endDate time.Time) ([]Sens
data.X = float64(xInt) / SCALING_FACTOR
data.Y = float64(yInt) / SCALING_FACTOR
data.Z = float64(zInt) / SCALING_FACTOR
+ data.Temperature = float64(tempInt) / SCALING_FACTOR
result = append(result, data)
}
@@ -190,12 +195,13 @@ func GetAllSensorIDs() ([]int, error) {
return ids, nil
}
-// SensorData 结构用于存储传感器数据
+// SensorData 结构用于存储传感器数据,添加温度字段
type SensorData struct {
- ID int `json:"id"`
- SensorID int `json:"sensor_id"`
- X float64 `json:"x"`
- Y float64 `json:"y"`
- Z float64 `json:"z"`
- Timestamp time.Time `json:"timestamp"`
+ ID int `json:"id"`
+ SensorID int `json:"sensor_id"`
+ X float64 `json:"x"`
+ Y float64 `json:"y"`
+ Z float64 `json:"z"`
+ Temperature float64 `json:"temperature"`
+ Timestamp time.Time `json:"timestamp"`
}
diff --git a/tcp_server.go b/tcp_server.go
index 5a9aaa4..f7437d4 100644
--- a/tcp_server.go
+++ b/tcp_server.go
@@ -1,236 +1,292 @@
-package main
-
-import (
- "fmt"
- "io"
- "net"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-// 客户端信息结构
-type ClientInfo struct {
- IP string // IP地址
- Port string // 端口
- LastSeen time.Time // 最后活跃时间
-}
-
-// 客户端列表(使用互斥锁保护的映射)
-var (
- clientsMutex sync.Mutex
- clients = make(map[string]*ClientInfo)
-)
-
-// StartTCPServer 启动TCP服务器
-func StartTCPServer(address string) error {
- listener, err := net.Listen("tcp", address)
- if err != nil {
- return err
- }
-
- startClientCleanup()
-
- Logger.Printf("TCP服务器已启动,正在监听 %s\n", address)
-
- for {
- conn, err := listener.Accept()
- if err != nil {
- Logger.Printf("接受连接失败: %v", err)
- continue
- }
-
- go handleConnection(conn)
- }
-}
-
-// handleConnection 处理客户端连接
-func handleConnection(conn net.Conn) {
- defer conn.Close()
-
- remoteAddr := conn.RemoteAddr().String()
- Logger.Printf("新的客户端连接: %s", remoteAddr)
-
- addClient(remoteAddr)
-
- buffer := make([]byte, 1024)
-
- for {
- n, err := conn.Read(buffer)
- if err != nil {
- if err != io.EOF {
- Logger.Printf("从客户端读取失败 %s: %v", remoteAddr, err)
- } else {
- Logger.Printf("客户端断开连接 %s", remoteAddr)
- }
- removeClient(remoteAddr)
- break
- }
-
- rawData := string(buffer[:n])
- TCPDataLogger.Printf("从客户端 %s 接收到原始数据: %s", remoteAddr, rawData)
-
- sensorID, x, y, z, err := parseData(rawData)
-
- if err == nil {
- TCPDataLogger.Printf("解析成功 - 客户端: %s, 传感器ID: %d, 值: X=%.3f, Y=%.3f, Z=%.3f",
- remoteAddr, sensorID, x, y, z)
-
- if err := SaveSensorData(sensorID, x, y, z); err != nil {
- Logger.Printf("保存传感器数据失败: %v", err)
- }
- } else {
- TCPDataLogger.Printf("无法解析从客户端 %s 接收到的数据: %s, 错误: %v", remoteAddr, rawData, err)
- }
-
- resp := "OK\n"
- if _, err := conn.Write([]byte(resp)); err != nil {
- Logger.Printf("发送响应到客户端 %s 失败: %v", remoteAddr, err)
- removeClient(remoteAddr)
- break
- }
-
- updateClientLastSeen(remoteAddr)
- }
-}
-
-// parseData 使用正则表达式解析传感器数据
-func parseData(data string) (int, float64, float64, float64, error) {
- pattern := regexp.MustCompile(`(\d+):([-]?\d+\.\d+),\s*([-]?\d+\.\d+),\s*([-]?\d+\.\d+)`)
- matches := pattern.FindStringSubmatch(data)
-
- if len(matches) != 5 {
- return 0, 0, 0, 0, fmt.Errorf("数据格式不正确: %s", data)
- }
-
- sensorID, err := strconv.Atoi(matches[1])
- if err != nil {
- return 0, 0, 0, 0, fmt.Errorf("解析传感器ID失败: %v", err)
- }
-
- x, err := strconv.ParseFloat(strings.TrimSpace(matches[2]), 64)
- if err != nil {
- return 0, 0, 0, 0, fmt.Errorf("解析X值失败: %v", err)
- }
-
- y, err := strconv.ParseFloat(strings.TrimSpace(matches[3]), 64)
- if err != nil {
- return 0, 0, 0, 0, fmt.Errorf("解析Y值失败: %v", err)
- }
-
- z, err := strconv.ParseFloat(strings.TrimSpace(matches[4]), 64)
- if err != nil {
- return 0, 0, 0, 0, fmt.Errorf("解析Z值失败: %v", err)
- }
-
- return sensorID, x, y, z, nil
-}
-
-// addClient 添加客户端
-func addClient(addr string) {
- clientsMutex.Lock()
- defer clientsMutex.Unlock()
-
- host, port, err := net.SplitHostPort(addr)
- if err != nil {
- Logger.Printf("解析客户端地址失败 %s: %v", addr, err)
- host = addr
- port = "unknown"
- }
-
- clients[addr] = &ClientInfo{
- IP: host,
- Port: port,
- LastSeen: time.Now(),
- }
-
- Logger.Printf("添加新客户端: %s", addr)
-}
-
-// updateClientLastSeen 更新客户端最后活跃时间
-func updateClientLastSeen(addr string) {
- clientsMutex.Lock()
- defer clientsMutex.Unlock()
-
- if client, exists := clients[addr]; exists {
- client.LastSeen = time.Now()
- }
-}
-
-// removeClient 移除客户端
-func removeClient(addr string) {
- clientsMutex.Lock()
- defer clientsMutex.Unlock()
-
- if client, exists := clients[addr]; exists {
- client.LastSeen = time.Now()
- Logger.Printf("客户端标记为断开连接: %s", addr)
- }
-}
-
-// getAllClients 获取所有客户端信息
-func getAllClients() []map[string]interface{} {
- clientsMutex.Lock()
- defer clientsMutex.Unlock()
-
- now := time.Now()
- result := make([]map[string]interface{}, 0, len(clients))
-
- for addr, client := range clients {
- lastSeenDuration := now.Sub(client.LastSeen)
-
- if lastSeenDuration > 24*time.Hour {
- delete(clients, addr)
- continue
- }
-
- isOnline := lastSeenDuration < 10*time.Minute
-
- result = append(result, map[string]interface{}{
- "address": addr,
- "ip": client.IP,
- "port": client.Port,
- "lastSeen": client.LastSeen,
- "isOnline": isOnline,
- "lastSeenFormatted": formatDuration(lastSeenDuration),
- })
- }
-
- return result
-}
-
-// formatDuration 格式化持续时间为友好的字符串
-func formatDuration(d time.Duration) string {
- if d < time.Minute {
- return "刚刚"
- } else if d < time.Hour {
- return fmt.Sprintf("%d分钟前", int(d.Minutes()))
- } else if d < 24*time.Hour {
- return fmt.Sprintf("%d小时前", int(d.Hours()))
- } else {
- return fmt.Sprintf("%d天前", int(d.Hours()/24))
- }
-}
-
-// startClientCleanup 启动清理过期客户端的goroutine
-func startClientCleanup() {
- go func() {
- for {
- time.Sleep(1 * time.Hour) // 每小时检查一次
-
- clientsMutex.Lock()
- now := time.Now()
-
- for addr, client := range clients {
- if now.Sub(client.LastSeen) > 24*time.Hour {
- delete(clients, addr)
- Logger.Printf("移除过期客户端: %s", addr)
- }
- }
-
- clientsMutex.Unlock()
- }
- }()
-}
+package main
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// 客户端信息结构
+type ClientInfo struct {
+ IP string // IP地址
+ Port string // 端口
+ LastSeen time.Time // 最后活跃时间
+}
+
+// 客户端列表(使用互斥锁保护的映射)
+var (
+ clientsMutex sync.Mutex
+ clients = make(map[string]*ClientInfo)
+)
+
+// StartTCPServer 启动TCP服务器
+func StartTCPServer(address string) error {
+ listener, err := net.Listen("tcp", address)
+ if err != nil {
+ return err
+ }
+
+ startClientCleanup()
+
+ Logger.Printf("TCP服务器已启动,正在监听 %s\n", address)
+
+ for {
+ conn, err := listener.Accept()
+ if err != nil {
+ Logger.Printf("接受连接失败: %v", err)
+ continue
+ }
+
+ go handleConnection(conn)
+ }
+}
+
+// handleConnection 处理客户端连接
+func handleConnection(conn net.Conn) {
+ defer conn.Close()
+
+ remoteAddr := conn.RemoteAddr().String()
+ Logger.Printf("新的客户端连接: %s", remoteAddr)
+
+ addClient(remoteAddr)
+
+ // 启动定时发送指令的goroutine
+ go sendPeriodicCommand(conn, remoteAddr)
+
+ buffer := make([]byte, 1024)
+
+ for {
+ n, err := conn.Read(buffer)
+ if err != nil {
+ if err != io.EOF {
+ Logger.Printf("从客户端读取失败 %s: %v", remoteAddr, err)
+ } else {
+ Logger.Printf("客户端断开连接 %s", remoteAddr)
+ }
+ removeClient(remoteAddr)
+ break
+ }
+
+ rawData := string(buffer[:n])
+ TCPDataLogger.Printf("从客户端 %s 接收到原始数据: %s", remoteAddr, rawData)
+
+ sensorID, x, y, z, temperature, err := parseData(rawData)
+
+ if err == nil {
+ TCPDataLogger.Printf("解析成功 - 客户端: %s, 传感器ID: %d, 值: X=%.3f, Y=%.3f, Z=%.3f, 温度=%.1f°C",
+ remoteAddr, sensorID, x, y, z, temperature)
+
+ if err := SaveSensorData(sensorID, x, y, z, temperature); err != nil {
+ Logger.Printf("保存传感器数据失败: %v", err)
+ }
+ } else {
+ TCPDataLogger.Printf("无法解析从客户端 %s 接收到的数据: %s, 错误: %v", remoteAddr, rawData, err)
+ }
+
+ resp := "OK\n"
+ if _, err := conn.Write([]byte(resp)); err != nil {
+ Logger.Printf("发送响应到客户端 %s 失败: %v", remoteAddr, err)
+ removeClient(remoteAddr)
+ break
+ }
+
+ updateClientLastSeen(remoteAddr)
+ }
+}
+
+// sendPeriodicCommand 每10秒发送一次查询指令
+func sendPeriodicCommand(conn net.Conn, remoteAddr string) {
+ ticker := time.NewTicker(10 * time.Second)
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ command := "@1602301014A*0!\n"
+ if _, err := conn.Write([]byte(command)); err != nil {
+ Logger.Printf("发送定时指令到客户端 %s 失败: %v", remoteAddr, err)
+ return // 连接断开,退出goroutine
+ }
+ TCPDataLogger.Printf("发送定时指令到客户端 %s: %s", remoteAddr, strings.TrimSpace(command))
+ }
+ }
+}
+
+// parseData 使用正则表达式解析传感器数据,支持新格式 #{1602301014-01,1,1,28.4,-6.884,1.540}!
+func parseData(data string) (int, float64, float64, float64, float64, error) {
+ // 尝试解析新格式: #{1602301014-01,1,1,28.4,-6.884,1.540}!
+ newPattern := regexp.MustCompile(`#\{[^,]+-(\d+),\d+,(\d+),([-]?\d+\.\d+),([-]?\d+\.\d+),([-]?\d+\.\d+)\}!`)
+ matches := newPattern.FindStringSubmatch(data)
+
+ if len(matches) == 6 {
+ // 新格式解析
+ sensorID, err := strconv.Atoi(matches[2]) // 使用传感器地址编号
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析传感器ID失败: %v", err)
+ }
+
+ temperature, err := strconv.ParseFloat(strings.TrimSpace(matches[3]), 64)
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析温度值失败: %v", err)
+ }
+
+ x, err := strconv.ParseFloat(strings.TrimSpace(matches[4]), 64)
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析X值失败: %v", err)
+ }
+
+ y, err := strconv.ParseFloat(strings.TrimSpace(matches[5]), 64)
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析Y值失败: %v", err)
+ }
+
+ z := 0.0 // 新格式没有Z值,设为0
+
+ return sensorID, x, y, z, temperature, nil
+ }
+
+ // 尝试解析旧格式: 1:1.000, 2.000, 3.000
+ oldPattern := regexp.MustCompile(`(\d+):([-]?\d+\.\d+),\s*([-]?\d+\.\d+),\s*([-]?\d+\.\d+)`)
+ matches = oldPattern.FindStringSubmatch(data)
+
+ if len(matches) == 5 {
+ // 旧格式解析
+ sensorID, err := strconv.Atoi(matches[1])
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析传感器ID失败: %v", err)
+ }
+
+ x, err := strconv.ParseFloat(strings.TrimSpace(matches[2]), 64)
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析X值失败: %v", err)
+ }
+
+ y, err := strconv.ParseFloat(strings.TrimSpace(matches[3]), 64)
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析Y值失败: %v", err)
+ }
+
+ z, err := strconv.ParseFloat(strings.TrimSpace(matches[4]), 64)
+ if err != nil {
+ return 0, 0, 0, 0, 0, fmt.Errorf("解析Z值失败: %v", err)
+ }
+
+ temperature := 0.0 // 旧格式没有温度值,设为0
+
+ return sensorID, x, y, z, temperature, nil
+ }
+
+ return 0, 0, 0, 0, 0, fmt.Errorf("数据格式不正确: %s", data)
+}
+
+// addClient 添加客户端
+func addClient(addr string) {
+ clientsMutex.Lock()
+ defer clientsMutex.Unlock()
+
+ host, port, err := net.SplitHostPort(addr)
+ if err != nil {
+ Logger.Printf("解析客户端地址失败 %s: %v", addr, err)
+ host = addr
+ port = "unknown"
+ }
+
+ clients[addr] = &ClientInfo{
+ IP: host,
+ Port: port,
+ LastSeen: time.Now(),
+ }
+
+ Logger.Printf("添加新客户端: %s", addr)
+}
+
+// updateClientLastSeen 更新客户端最后活跃时间
+func updateClientLastSeen(addr string) {
+ clientsMutex.Lock()
+ defer clientsMutex.Unlock()
+
+ if client, exists := clients[addr]; exists {
+ client.LastSeen = time.Now()
+ }
+}
+
+// removeClient 移除客户端
+func removeClient(addr string) {
+ clientsMutex.Lock()
+ defer clientsMutex.Unlock()
+
+ if client, exists := clients[addr]; exists {
+ client.LastSeen = time.Now()
+ Logger.Printf("客户端标记为断开连接: %s", addr)
+ }
+}
+
+// getAllClients 获取所有客户端信息
+func getAllClients() []map[string]interface{} {
+ clientsMutex.Lock()
+ defer clientsMutex.Unlock()
+
+ now := time.Now()
+ result := make([]map[string]interface{}, 0, len(clients))
+
+ for addr, client := range clients {
+ lastSeenDuration := now.Sub(client.LastSeen)
+
+ if lastSeenDuration > 24*time.Hour {
+ delete(clients, addr)
+ continue
+ }
+
+ isOnline := lastSeenDuration < 10*time.Minute
+
+ result = append(result, map[string]interface{}{
+ "address": addr,
+ "ip": client.IP,
+ "port": client.Port,
+ "lastSeen": client.LastSeen,
+ "isOnline": isOnline,
+ "lastSeenFormatted": formatDuration(lastSeenDuration),
+ })
+ }
+
+ return result
+}
+
+// formatDuration 格式化持续时间为友好的字符串
+func formatDuration(d time.Duration) string {
+ if d < time.Minute {
+ return "刚刚"
+ } else if d < time.Hour {
+ return fmt.Sprintf("%d分钟前", int(d.Minutes()))
+ } else if d < 24*time.Hour {
+ return fmt.Sprintf("%d小时前", int(d.Hours()))
+ } else {
+ return fmt.Sprintf("%d天前", int(d.Hours()/24))
+ }
+}
+
+// startClientCleanup 启动清理过期客户端的goroutine
+func startClientCleanup() {
+ go func() {
+ for {
+ time.Sleep(1 * time.Hour) // 每小时检查一次
+
+ clientsMutex.Lock()
+ now := time.Now()
+
+ for addr, client := range clients {
+ if now.Sub(client.LastSeen) > 24*time.Hour {
+ delete(clients, addr)
+ Logger.Printf("移除过期客户端: %s", addr)
+ }
+ }
+
+ clientsMutex.Unlock()
+ }
+ }()
+}
diff --git a/templates/index.html b/templates/index.html
index c2186fa..3a9287f 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -201,11 +201,12 @@
数据编号
- 探头地址
+ 探头地址
时间
X
Y
Z
+ 温度(°C)