feat: 新的解析函数
This commit is contained in:
parent
07b5cb9843
commit
a2d0bc8226
40
db.go
40
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"`
|
||||
}
|
||||
|
||||
528
tcp_server.go
528
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()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@ -201,11 +201,12 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>数据编号</th>
|
||||
<th>探头地址</th>
|
||||
<th>探头地址</th>
|
||||
<th>时间</th>
|
||||
<th>X</th>
|
||||
<th>Y</th>
|
||||
<th>Z</th>
|
||||
<th>温度(°C)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tableBody">
|
||||
@ -439,7 +440,7 @@
|
||||
|
||||
if (data.length === 0) {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = '<td colspan="6" style="text-align: center;">没有数据</td>';
|
||||
row.innerHTML = '<td colspan="7" style="text-align: center;">没有数据</td>';
|
||||
tableBody.appendChild(row);
|
||||
return;
|
||||
}
|
||||
@ -467,7 +468,8 @@
|
||||
'<td>' + formattedDate + '</td>' +
|
||||
'<td>' + item.x.toFixed(3) + '</td>' +
|
||||
'<td>' + item.y.toFixed(3) + '</td>' +
|
||||
'<td>' + item.z.toFixed(3) + '</td>' ;
|
||||
'<td>' + item.z.toFixed(3) + '</td>' +
|
||||
'<td>' + item.temperature.toFixed(1) + '</td>';
|
||||
|
||||
tableBody.appendChild(row);
|
||||
});
|
||||
@ -579,11 +581,12 @@
|
||||
'rgb(255, 159, 64)'
|
||||
];
|
||||
|
||||
// 为X, Y, Z创建不同的数据集
|
||||
// 为X, Y, Z, 温度创建不同的数据集
|
||||
const dataTypes = [
|
||||
{ key: 'x', label: 'X' },
|
||||
{ key: 'y', label: 'Y' },
|
||||
{ key: 'z', label: 'Z' }
|
||||
{ key: 'z', label: 'Z' },
|
||||
{ key: 'temperature', label: '温度(°C)' }
|
||||
];
|
||||
|
||||
sensorIDs.forEach((sensorID, sensorIndex) => {
|
||||
@ -616,7 +619,7 @@
|
||||
}
|
||||
|
||||
// 准备CSV内容
|
||||
let csvContent = "数据编号,探头地址编号,X,Y,Z,时间\n";
|
||||
let csvContent = "数据编号,探头地址编号,X,Y,Z,温度,时间\n";
|
||||
|
||||
currentSensorData.forEach(item => {
|
||||
// 解析时间并调整为中国时间
|
||||
@ -632,14 +635,13 @@
|
||||
date.getMinutes().toString().padStart(2, '0') + ':' +
|
||||
date.getSeconds().toString().padStart(2, '0');
|
||||
|
||||
// 添加一行数据
|
||||
csvContent +=
|
||||
item.id + "," +
|
||||
item.sensor_id + "," +
|
||||
item.x.toFixed(3) + "," +
|
||||
item.y.toFixed(3) + "," +
|
||||
item.z.toFixed(3) + "," +
|
||||
formattedDate + "\n";
|
||||
csvContent += item.id + "," +
|
||||
item.sensor_id + "," +
|
||||
item.x.toFixed(3) + "," +
|
||||
item.y.toFixed(3) + "," +
|
||||
item.z.toFixed(3) + "," +
|
||||
item.temperature.toFixed(1) + "," +
|
||||
formattedDate + "\n";
|
||||
});
|
||||
|
||||
// 创建Blob对象
|
||||
|
||||
@ -44,13 +44,13 @@ func handleUDPPacket(conn *net.UDPConn, addr *net.UDPAddr, data []byte) {
|
||||
rawData := string(data)
|
||||
TCPDataLogger.Printf("从UDP客户端 %s 接收到原始数据: %s", remoteAddr, rawData)
|
||||
|
||||
sensorID, x, y, z, err := parseData(rawData)
|
||||
sensorID, x, y, z, temperature, err := parseData(rawData)
|
||||
|
||||
if err == nil {
|
||||
TCPDataLogger.Printf("解析成功 - UDP客户端: %s, 传感器ID: %d, 值: X=%.3f, Y=%.3f, Z=%.3f",
|
||||
remoteAddr, sensorID, x, y, z)
|
||||
TCPDataLogger.Printf("解析成功 - UDP客户端: %s, 传感器ID: %d, 值: X=%.3f, Y=%.3f, Z=%.3f, 温度=%.1f°C",
|
||||
remoteAddr, sensorID, x, y, z, temperature)
|
||||
|
||||
if err := SaveSensorData(sensorID, x, y, z); err != nil {
|
||||
if err := SaveSensorData(sensorID, x, y, z, temperature); err != nil {
|
||||
Logger.Printf("保存传感器数据失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user