事件触发类型改为按照坐标触发
This commit is contained in:
		
							parent
							
								
									130b8db17a
								
							
						
					
					
						commit
						b91e3f5bfb
					
				
							
								
								
									
										2
									
								
								.idea/.name
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/.name
									
									
									
										generated
									
									
									
								
							@ -1 +1 @@
 | 
				
			|||||||
SimTruckService.go
 | 
					simulation_service.go
 | 
				
			||||||
@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
@ -73,7 +74,6 @@ type SimulationParams struct {
 | 
				
			|||||||
	CoalPickupOrder string  `json:"coal_pickup_order"` // 固定提煤单
 | 
						CoalPickupOrder string  `json:"coal_pickup_order"` // 固定提煤单
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Truck 表示一辆卡车
 | 
					 | 
				
			||||||
type Truck struct {
 | 
					type Truck struct {
 | 
				
			||||||
	ID              int
 | 
						ID              int
 | 
				
			||||||
	VIN             string
 | 
						VIN             string
 | 
				
			||||||
@ -95,6 +95,7 @@ type Truck struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	targetPointReachedCount int32
 | 
						targetPointReachedCount int32
 | 
				
			||||||
	eventMutex              sync.Mutex
 | 
						eventMutex              sync.Mutex
 | 
				
			||||||
 | 
						triggeredEvents         map[string]bool // 记录已触发的事件类型
 | 
				
			||||||
	eventsSent              map[int]bool
 | 
						eventsSent              map[int]bool
 | 
				
			||||||
	currentEventIndex       int
 | 
						currentEventIndex       int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -124,6 +125,170 @@ var additionalPoints = []RoutePoint{
 | 
				
			|||||||
	{Longitude: 90.625132, Latitude: 43.802499, Attribute: 4},
 | 
						{Longitude: 90.625132, Latitude: 43.802499, Attribute: 4},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// EventTriggerPoint 定义触发点及其关联事件
 | 
				
			||||||
 | 
					type EventTriggerPoint struct {
 | 
				
			||||||
 | 
						Longitude float64
 | 
				
			||||||
 | 
						Latitude  float64
 | 
				
			||||||
 | 
						EventType string  // 对应事件类型 "0", "1", "2" 等
 | 
				
			||||||
 | 
						Tolerance float64 // 判定到达的容差范围
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 为每条路线定义触发点
 | 
				
			||||||
 | 
					// 为每条路线定义触发点
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// Route1(G7干线)的触发点
 | 
				
			||||||
 | 
						route1TriggerPoints = []EventTriggerPoint{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.172811, // 起点 - 创建运单
 | 
				
			||||||
 | 
								Latitude:  44.859511,
 | 
				
			||||||
 | 
								EventType: "0",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.316852, // 进陆港前 - 司机接单
 | 
				
			||||||
 | 
								Latitude:  44.762968,
 | 
				
			||||||
 | 
								EventType: "1",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.365738, // 进陆港
 | 
				
			||||||
 | 
								Latitude:  44.768674,
 | 
				
			||||||
 | 
								EventType: "2",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.381814, // 领取货箱
 | 
				
			||||||
 | 
								Latitude:  44.769508,
 | 
				
			||||||
 | 
								EventType: "3",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.413458, // 出陆港
 | 
				
			||||||
 | 
								Latitude:  44.770878,
 | 
				
			||||||
 | 
								EventType: "4",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.552294, // 收费站
 | 
				
			||||||
 | 
								Latitude:  44.762026,
 | 
				
			||||||
 | 
								EventType: "5",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.408540, // 开始排队
 | 
				
			||||||
 | 
								Latitude:  43.868251,
 | 
				
			||||||
 | 
								EventType: "6",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.484399, // 装运
 | 
				
			||||||
 | 
								Latitude:  43.842926,
 | 
				
			||||||
 | 
								EventType: "7",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.498991, // 收费站返程
 | 
				
			||||||
 | 
								Latitude:  43.841625,
 | 
				
			||||||
 | 
								EventType: "8",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.515191, // 进陆港返程
 | 
				
			||||||
 | 
								Latitude:  43.839318,
 | 
				
			||||||
 | 
								EventType: "9",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.525162, // 陆港卸货
 | 
				
			||||||
 | 
								Latitude:  43.838095,
 | 
				
			||||||
 | 
								EventType: "10",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.535948, // 终点 - 运单结束
 | 
				
			||||||
 | 
								Latitude:  43.835969,
 | 
				
			||||||
 | 
								EventType: "11",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Route2(短倒/支线)的触发点
 | 
				
			||||||
 | 
						route2TriggerPoints = []EventTriggerPoint{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.153942, // 起点 - 创建运单
 | 
				
			||||||
 | 
								Latitude:  44.757513,
 | 
				
			||||||
 | 
								EventType: "0",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.174784, // 司机接单
 | 
				
			||||||
 | 
								Latitude:  44.78754,
 | 
				
			||||||
 | 
								EventType: "1",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.196045, // 进陆港
 | 
				
			||||||
 | 
								Latitude:  44.81793,
 | 
				
			||||||
 | 
								EventType: "2",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.214009, // 领取货箱
 | 
				
			||||||
 | 
								Latitude:  44.838927,
 | 
				
			||||||
 | 
								EventType: "3",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.237319, // 出陆港
 | 
				
			||||||
 | 
								Latitude:  44.854165,
 | 
				
			||||||
 | 
								EventType: "4",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 89.23939, // 收费站
 | 
				
			||||||
 | 
								Latitude:  44.855511,
 | 
				
			||||||
 | 
								EventType: "5",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.600018, // 开始排队
 | 
				
			||||||
 | 
								Latitude:  44.676285,
 | 
				
			||||||
 | 
								EventType: "6",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.616385, // 装运
 | 
				
			||||||
 | 
								Latitude:  44.269544,
 | 
				
			||||||
 | 
								EventType: "7",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.617192, // 收费站返程
 | 
				
			||||||
 | 
								Latitude:  44.075327,
 | 
				
			||||||
 | 
								EventType: "8",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.621784, // 进陆港返程
 | 
				
			||||||
 | 
								Latitude:  44.052461,
 | 
				
			||||||
 | 
								EventType: "9",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.624094, // 陆港卸货
 | 
				
			||||||
 | 
								Latitude:  44.040623,
 | 
				
			||||||
 | 
								EventType: "10",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Longitude: 90.625132, // 终点 - 运单结束
 | 
				
			||||||
 | 
								Latitude:  43.802499,
 | 
				
			||||||
 | 
								EventType: "11",
 | 
				
			||||||
 | 
								Tolerance: 0.001,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MQTT相关和全局变量
 | 
					// MQTT相关和全局变量
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	mqttBroker     = "tcp://123.6.102.119:1883"
 | 
						mqttBroker     = "tcp://123.6.102.119:1883"
 | 
				
			||||||
@ -531,6 +696,18 @@ func sendEvent(mqttClient mqtt.Client, event map[string]interface{}) {
 | 
				
			|||||||
	}()
 | 
						}()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getTriggerPoints 根据路线名称获取对应的触发点
 | 
				
			||||||
 | 
					func getTriggerPoints(routeName string) []EventTriggerPoint {
 | 
				
			||||||
 | 
						switch routeName {
 | 
				
			||||||
 | 
						case Route1Name:
 | 
				
			||||||
 | 
							return route1TriggerPoints
 | 
				
			||||||
 | 
						case Route2Name:
 | 
				
			||||||
 | 
							return route2TriggerPoints
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// sendInitialEvent 发送初始事件,根据 RouteName 选择事件类型 "0_route1" 或 "0_route2"
 | 
					// sendInitialEvent 发送初始事件,根据 RouteName 选择事件类型 "0_route1" 或 "0_route2"
 | 
				
			||||||
func (t *Truck) sendInitialEvent(coalPickupOrder string) {
 | 
					func (t *Truck) sendInitialEvent(coalPickupOrder string) {
 | 
				
			||||||
	var initialEvents []map[string]interface{}
 | 
						var initialEvents []map[string]interface{}
 | 
				
			||||||
@ -704,36 +881,25 @@ func (t *Truck) runSimulation(stopChan chan struct{}, mainOrderNumber string, co
 | 
				
			|||||||
		activeTrucksMutex.Unlock()
 | 
							activeTrucksMutex.Unlock()
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 使用传入的主订单号作为 OrderID
 | 
						// 设置基本信息
 | 
				
			||||||
	t.OrderID = mainOrderNumber
 | 
						t.OrderID = mainOrderNumber
 | 
				
			||||||
	log.Printf("卡车 %d 订单号: %s", t.ID, t.OrderID)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 设置固定的提煤单
 | 
					 | 
				
			||||||
	t.CoalPickupOrder = coalPickupOrder
 | 
						t.CoalPickupOrder = coalPickupOrder
 | 
				
			||||||
	log.Printf("卡车 %d 提煤单: %s", t.ID, t.CoalPickupOrder)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 生成运单
 | 
					 | 
				
			||||||
	t.Waybill = generateWaybill(mainOrderNumber)
 | 
						t.Waybill = generateWaybill(mainOrderNumber)
 | 
				
			||||||
	log.Printf("卡车 %d 生成运单: %s", t.ID, t.Waybill)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 生成固定的 VIN 码
 | 
					 | 
				
			||||||
	t.VIN = generateFixedVIN()
 | 
						t.VIN = generateFixedVIN()
 | 
				
			||||||
	log.Printf("卡车 %d 生成 VIN: %s", t.ID, t.VIN)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 发送所有事件(包括创建运单事件和后续事件)
 | 
						// 初始化事件追踪
 | 
				
			||||||
	log.Printf("卡车 %d 开始发送所有事件", t.ID)
 | 
						t.eventMutex.Lock()
 | 
				
			||||||
	t.sendAllEvents()
 | 
						t.triggeredEvents = make(map[string]bool)
 | 
				
			||||||
 | 
						t.eventMutex.Unlock()
 | 
				
			||||||
	// 添加日志,确认即将发送异常状态事件
 | 
					 | 
				
			||||||
	log.Printf("卡车 %d 即将发送50条异常状态事件", t.ID)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 发送50条异常状态事件
 | 
						// 发送50条异常状态事件
 | 
				
			||||||
 | 
						log.Printf("卡车 %d 开始发送50条异常状态事件", t.ID)
 | 
				
			||||||
	t.sendAbnormalEvents()
 | 
						t.sendAbnormalEvents()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 设置初始事件索引
 | 
						// 设置初始事件索引
 | 
				
			||||||
	t.currentEventIndex = 0
 | 
						t.currentEventIndex = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 等待10秒后开始执行事件
 | 
						// 等待10秒后开始执行行驶模拟
 | 
				
			||||||
	log.Printf("卡车 %d 等待10秒后开始执行行驶模拟", t.ID)
 | 
						log.Printf("卡车 %d 等待10秒后开始执行行驶模拟", t.ID)
 | 
				
			||||||
	select {
 | 
						select {
 | 
				
			||||||
	case <-time.After(10 * time.Second):
 | 
						case <-time.After(10 * time.Second):
 | 
				
			||||||
@ -741,7 +907,7 @@ func (t *Truck) runSimulation(stopChan chan struct{}, mainOrderNumber string, co
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 往返循环开始行驶模拟...
 | 
						// 往返循环开始行驶模拟
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case <-stopChan:
 | 
							case <-stopChan:
 | 
				
			||||||
@ -767,6 +933,11 @@ func (t *Truck) runSimulation(stopChan chan struct{}, mainOrderNumber string, co
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			reverseRoute := reverseRoute(t.Route)
 | 
								reverseRoute := reverseRoute(t.Route)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 重置事件触发状态用于返程
 | 
				
			||||||
 | 
								t.eventMutex.Lock()
 | 
				
			||||||
 | 
								t.triggeredEvents = make(map[string]bool)
 | 
				
			||||||
 | 
								t.eventMutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !t.runRouteSegment(stopChan, reverseRoute) {
 | 
								if !t.runRouteSegment(stopChan, reverseRoute) {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -778,69 +949,114 @@ func (t *Truck) runSimulation(stopChan chan struct{}, mainOrderNumber string, co
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// sendAllEvents 按顺序发送所有事件
 | 
					// checkAndTriggerEvent 检查并触发事件
 | 
				
			||||||
func (t *Truck) sendAllEvents() {
 | 
					func (t *Truck) checkAndTriggerEvent(currentPoint RoutePoint) {
 | 
				
			||||||
	// 事件类型按顺序存储在一个切片中
 | 
						// 获取当前路线的触发点
 | 
				
			||||||
	eventTypes := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}
 | 
						triggerPoints := getTriggerPoints(t.RouteName)
 | 
				
			||||||
 | 
						if triggerPoints == nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, eventType := range eventTypes {
 | 
						t.eventMutex.Lock()
 | 
				
			||||||
		// 获取当前事件类型的所有事件
 | 
						defer t.eventMutex.Unlock()
 | 
				
			||||||
		events := getRouteEvents(t.RouteName, eventType)
 | 
					
 | 
				
			||||||
		if len(events) == 0 {
 | 
						for _, triggerPoint := range triggerPoints {
 | 
				
			||||||
			log.Printf("卡车 %d 没有事件类型 %s 的事件定义", t.ID, eventType)
 | 
							// 如果已触发则跳过
 | 
				
			||||||
 | 
							if t.triggeredEvents[triggerPoint.EventType] {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 遍历该类型的所有事件并发送
 | 
							// 检查是否在触发点附近
 | 
				
			||||||
		for _, event := range events {
 | 
							if math.Abs(currentPoint.Longitude-triggerPoint.Longitude) <= triggerPoint.Tolerance &&
 | 
				
			||||||
			// 深拷贝事件
 | 
								math.Abs(currentPoint.Latitude-triggerPoint.Latitude) <= triggerPoint.Tolerance {
 | 
				
			||||||
			eventCopy := cloneEvent(event)
 | 
					
 | 
				
			||||||
			if eventCopy == nil {
 | 
								// 标记事件已触发
 | 
				
			||||||
 | 
								t.triggeredEvents[triggerPoint.EventType] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 确保事件按顺序触发(除了事件0以外)
 | 
				
			||||||
 | 
								eventNum, _ := strconv.Atoi(triggerPoint.EventType)
 | 
				
			||||||
 | 
								if eventNum > 0 {
 | 
				
			||||||
 | 
									prevEvent := strconv.Itoa(eventNum - 1)
 | 
				
			||||||
 | 
									if !t.triggeredEvents[prevEvent] {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 获取事件模板
 | 
				
			||||||
 | 
								events := getRouteEvents(t.RouteName, triggerPoint.EventType)
 | 
				
			||||||
 | 
								if len(events) == 0 {
 | 
				
			||||||
 | 
									log.Printf("卡车 %d: 路线 %s 未找到事件类型 %s 的事件定义",
 | 
				
			||||||
 | 
										t.ID, t.RouteName, triggerPoint.EventType)
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// 更新事件的动态字段
 | 
								// 克隆事件并填充信息
 | 
				
			||||||
			eventCopy["车牌"] = t.CarPlate
 | 
								event := cloneEvent(events[0])
 | 
				
			||||||
			eventCopy["司机"] = t.DriverName
 | 
								if event == nil {
 | 
				
			||||||
			eventCopy["vin"] = t.VIN
 | 
									continue
 | 
				
			||||||
			eventCopy["订单"] = t.OrderID
 | 
					 | 
				
			||||||
			eventCopy["运单"] = t.Waybill
 | 
					 | 
				
			||||||
			eventCopy["time"] = time.Now().Format("2006-01-02 15:04:05")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// 如果有货箱编号字段,设置它
 | 
					 | 
				
			||||||
			if _, exists := eventCopy["货箱编号"]; exists {
 | 
					 | 
				
			||||||
				eventCopy["货箱编号"] = "TRA1205B"
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// 如果有净重字段,设置它
 | 
								// 更新事件基本信息
 | 
				
			||||||
			if _, exists := eventCopy["净重吨数"]; exists {
 | 
								event["车牌"] = t.CarPlate
 | 
				
			||||||
				eventCopy["净重吨数"] = t.LoadWeight
 | 
								event["司机"] = t.DriverName
 | 
				
			||||||
 | 
								event["vin"] = t.VIN
 | 
				
			||||||
 | 
								event["订单"] = t.OrderID
 | 
				
			||||||
 | 
								event["运单"] = t.Waybill
 | 
				
			||||||
 | 
								event["time"] = time.Now().Format("2006-01-02 15:04:05")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 更新位置信息
 | 
				
			||||||
 | 
								if _, hasPos := event["pos"]; hasPos {
 | 
				
			||||||
 | 
									event["pos"] = []float64{currentPoint.Longitude, currentPoint.Latitude}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if _, exists := eventCopy["提煤单"]; exists {
 | 
								// 根据路线类型设置运输方式
 | 
				
			||||||
				eventCopy["提煤单"] = t.CoalPickupOrder
 | 
								if t.RouteName == Route1Name {
 | 
				
			||||||
 | 
									if _, hasType := event["线路类型"]; hasType {
 | 
				
			||||||
 | 
										event["线路类型"] = TransportModeTrunk // 干线
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if t.RouteName == Route2Name {
 | 
				
			||||||
 | 
									if _, hasType := event["线路类型"]; hasType {
 | 
				
			||||||
 | 
										event["线路类型"] = TransportModeShortHaul // 短倒
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if pos, exists := eventCopy["pos"].([]interface{}); exists {
 | 
								// 设置其他可选字段
 | 
				
			||||||
				// 使用当前位置更新pos字段
 | 
								if _, exists := event["货箱编号"]; exists {
 | 
				
			||||||
				currentPoint := t.Route[0] // 使用路线的第一个点作为初始位置
 | 
									event["货箱编号"] = "TRA1205B"
 | 
				
			||||||
				pos[0] = currentPoint.Longitude
 | 
								}
 | 
				
			||||||
				pos[1] = currentPoint.Latitude
 | 
								if _, exists := event["净重吨数"]; exists {
 | 
				
			||||||
				eventCopy["pos"] = pos
 | 
									event["净重吨数"] = fmt.Sprintf("%.2f", t.LoadWeight)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if _, exists := event["提煤单"]; exists {
 | 
				
			||||||
 | 
									event["提煤单"] = t.CoalPickupOrder
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// 发送事件
 | 
								// 发送事件
 | 
				
			||||||
			sendEvent(t.MQTTClient, eventCopy)
 | 
								sendEvent(t.MQTTClient, event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// 在事件之间添加延迟,让事件按序发生
 | 
								// 记录日志
 | 
				
			||||||
			time.Sleep(5 * time.Second)
 | 
								log.Printf("卡车 %d (%s) 在位置 [%.6f, %.6f] 触发事件类型 %s",
 | 
				
			||||||
 | 
									t.ID, t.RouteName, currentPoint.Longitude, currentPoint.Latitude, triggerPoint.EventType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 特殊处理:如果是短倒路线且到达目标点,通知 G7 干线发车
 | 
				
			||||||
 | 
								if t.RouteName == Route2Name && triggerPoint.EventType == "3" {
 | 
				
			||||||
 | 
									atomic.AddInt32(&t.targetPointReachedCount, 1)
 | 
				
			||||||
 | 
									if atomic.LoadInt32(&t.targetPointReachedCount) == 2 {
 | 
				
			||||||
 | 
										select {
 | 
				
			||||||
 | 
										case route1StartChan <- struct{}{}:
 | 
				
			||||||
 | 
											log.Printf("卡车 %d: 短倒路线第二次到达目标点,通知 G7 干线发车", t.ID)
 | 
				
			||||||
 | 
										default:
 | 
				
			||||||
 | 
											// 如果通道已满,避免阻塞
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// runRouteSegment 执行路线段的模拟(已移除基于位置的事件触发逻辑)
 | 
					// runRouteSegment 执行路线段的模拟
 | 
				
			||||||
func (t *Truck) runRouteSegment(stopChan chan struct{}, routeSegment []RoutePoint) bool {
 | 
					func (t *Truck) runRouteSegment(stopChan chan struct{}, routeSegment []RoutePoint) bool {
 | 
				
			||||||
	stepsBetweenPoints := 10
 | 
						stepsBetweenPoints := 5
 | 
				
			||||||
	for i := 0; i < len(routeSegment)-1; i++ {
 | 
						for i := 0; i < len(routeSegment)-1; i++ {
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case <-stopChan:
 | 
							case <-stopChan:
 | 
				
			||||||
@ -857,78 +1073,94 @@ func (t *Truck) runRouteSegment(stopChan chan struct{}, routeSegment []RoutePoin
 | 
				
			|||||||
					log.Printf("卡车 %d 模拟被停止", t.ID)
 | 
										log.Printf("卡车 %d 模拟被停止", t.ID)
 | 
				
			||||||
					return false
 | 
										return false
 | 
				
			||||||
				default:
 | 
									default:
 | 
				
			||||||
					// 移除基于位置的事件触发逻辑
 | 
										// 检查和触发基于位置的事件
 | 
				
			||||||
 | 
										t.checkAndTriggerEvent(pt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// 构建位置数据的 payload
 | 
										// 构建位置数据的 payload
 | 
				
			||||||
					speed := math.Round((20+80*rand.Float64())*10) / 10 // 定义 speed 变量
 | 
										speed := math.Round((20+80*rand.Float64())*10) / 10
 | 
				
			||||||
					vehicleType := "hydrogen"
 | 
										vehicleType := "hydrogen"
 | 
				
			||||||
					if t.RouteName == Route2Name {
 | 
										if t.RouteName == Route2Name {
 | 
				
			||||||
						vehicleType = "electric"
 | 
											vehicleType = "electric"
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// 更新卡车状态
 | 
				
			||||||
 | 
										t.Speed = speed
 | 
				
			||||||
 | 
										t.Mileage += speed * 0.0002 // 模拟里程增加
 | 
				
			||||||
 | 
										t.Location = fmt.Sprintf("%.6f,%.6f", pt.Longitude, pt.Latitude)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										// 获取司机状态
 | 
				
			||||||
 | 
										driverStatus := "正常"
 | 
				
			||||||
 | 
										driverStatusCategory := "run"
 | 
				
			||||||
 | 
										if status, found := getDriverStatusAtPoint(pt); found {
 | 
				
			||||||
 | 
											driverStatus = status
 | 
				
			||||||
 | 
											driverStatusCategory = mapDriverStatusToCategory(status)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					payload := map[string]interface{}{
 | 
										payload := map[string]interface{}{
 | 
				
			||||||
						"alarm_level":             0,
 | 
											"alarm_level":             0,
 | 
				
			||||||
						"charging_state":          0,
 | 
											"charging_state":          0,
 | 
				
			||||||
						"coal_pickup_order":       t.CoalPickupOrder,
 | 
											"coal_pickup_order":       t.CoalPickupOrder,
 | 
				
			||||||
						"cumulative_mileage":      15003.7, // 这里可以根据需求动态计算
 | 
											"cumulative_mileage":      roundTo(t.Mileage, 1),
 | 
				
			||||||
						"data_time":               time.Now().Format("2006-01-02 15:04:05"),
 | 
											"data_time":               time.Now().Format("2006-01-02 15:04:05"),
 | 
				
			||||||
						"driver_status":           "run",
 | 
											"driver_status":           driverStatusCategory,
 | 
				
			||||||
						"driver_status_original":  "正常",
 | 
											"driver_status_original":  driverStatus,
 | 
				
			||||||
						"general_alarm":           0,
 | 
											"general_alarm":           0,
 | 
				
			||||||
						"highest_temperature":     43,
 | 
											"highest_temperature":     43,
 | 
				
			||||||
						"highest_voltage_battery": 3.65,
 | 
											"highest_voltage_battery": 3.65,
 | 
				
			||||||
						"latitude":                pt.Latitude,  // 动态设置纬度
 | 
											"latitude":                pt.Latitude,
 | 
				
			||||||
						"longitude":               pt.Longitude, // 动态设置经度
 | 
											"longitude":               pt.Longitude,
 | 
				
			||||||
						"lowest_temperature":      22,
 | 
											"lowest_temperature":      22,
 | 
				
			||||||
						"lowest_voltage_battery":  3.29,
 | 
											"lowest_voltage_battery":  3.29,
 | 
				
			||||||
						"operation_mode":          0,
 | 
											"operation_mode":          0,
 | 
				
			||||||
						"order_id":                t.OrderID,     // 动态设置订单号
 | 
											"order_id":                t.OrderID,
 | 
				
			||||||
						"phonenumber":             t.PhoneNumber, // 动态设置电话号码
 | 
											"phonenumber":             t.PhoneNumber,
 | 
				
			||||||
						"soc":                     82,
 | 
											"soc":                     82,
 | 
				
			||||||
						"speed":                   speed, // 使用 speed 变量
 | 
											"speed":                   speed,
 | 
				
			||||||
						"subsystem_number":        1,
 | 
											"subsystem_number":        1,
 | 
				
			||||||
						"total_current":           13.4,
 | 
											"total_current":           13.4,
 | 
				
			||||||
						"total_voltage":           351.2,
 | 
											"total_voltage":           351.2,
 | 
				
			||||||
						"vehicle_id":              fmt.Sprintf("Truck-%d", t.ID), // 动态设置车辆ID
 | 
											"vehicle_id":              fmt.Sprintf("Truck-%d", t.ID),
 | 
				
			||||||
						"vehicle_state":           0,
 | 
											"vehicle_state":           0,
 | 
				
			||||||
						"vehicle_type":            vehicleType,
 | 
											"vehicle_type":            vehicleType,
 | 
				
			||||||
						"vin":                     t.VIN,     // 动态设置VIN
 | 
											"vin":                     t.VIN,
 | 
				
			||||||
						"waybill":                 t.Waybill, // 动态设置运单号
 | 
											"waybill":                 t.Waybill,
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// 根据状态设置 driver_status 和 driver_status_original
 | 
										// 根据车辆类型设置特定参数
 | 
				
			||||||
					// 由于已移除基于位置的事件触发,这里可以简化
 | 
										if t.RouteName == Route2Name {
 | 
				
			||||||
					// 如果需要,可以根据其他条件调整
 | 
											// 电动车特定参数
 | 
				
			||||||
 | 
											payload["soc"] = math.Max(20, 82-t.Mileage*0.01)           // SOC随里程降低
 | 
				
			||||||
 | 
											payload["total_current"] = 13.4 + rand.Float64()*2 - 1     // 随机波动的电流
 | 
				
			||||||
 | 
											payload["total_voltage"] = 351.2 + rand.Float64()*10 - 5   // 随机波动的电压
 | 
				
			||||||
 | 
											payload["highest_temperature"] = 43 + rand.Float64()*4 - 2 // 随机波动的温度
 | 
				
			||||||
 | 
											payload["lowest_temperature"] = 22 + rand.Float64()*4 - 2
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// 创建一个仅包含值的数组,按照固定顺序
 | 
										// 使用固定字段顺序构建数据数组
 | 
				
			||||||
					//logKeysSorted("data", payload)
 | 
					 | 
				
			||||||
					values := make([]interface{}, 0, len(dataFieldOrder))
 | 
										values := make([]interface{}, 0, len(dataFieldOrder))
 | 
				
			||||||
					for _, key := range dataFieldOrder {
 | 
										for _, key := range dataFieldOrder {
 | 
				
			||||||
						if val, exists := payload[key]; exists {
 | 
											if val, exists := payload[key]; exists {
 | 
				
			||||||
							values = append(values, val)
 | 
												values = append(values, val)
 | 
				
			||||||
						} else {
 | 
											} else {
 | 
				
			||||||
							// 如果某个字段缺失,根据需求填充默认值
 | 
					 | 
				
			||||||
							values = append(values, nil)
 | 
												values = append(values, nil)
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// JSON 编码数组
 | 
										// JSON 编码
 | 
				
			||||||
					jsonData, err := json.Marshal(values)
 | 
										jsonData, err := json.Marshal(values)
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						log.Printf("卡车 %d JSON 编码错误: %v", t.ID, err)
 | 
											log.Printf("卡车 %d JSON 编码错误: %v", t.ID, err)
 | 
				
			||||||
						continue
 | 
											continue
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// 发布到 MQTT 的 data 主题
 | 
										// 发布到 MQTT
 | 
				
			||||||
					token := t.MQTTClient.Publish(mqttTopic, 0, false, jsonData)
 | 
										token := t.MQTTClient.Publish(mqttTopic, 0, false, jsonData)
 | 
				
			||||||
					token.Wait()
 | 
										token.Wait()
 | 
				
			||||||
					if token.Error() != nil {
 | 
										if token.Error() != nil {
 | 
				
			||||||
						log.Printf("卡车 %d 发布 MQTT 数据消息失败: %v", t.ID, token.Error())
 | 
											log.Printf("卡车 %d 发布 MQTT 数据消息失败: %v", t.ID, token.Error())
 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						log.Printf("卡车 %d (%s) 发布数据: %s", t.ID, t.RouteName, string(jsonData))
 | 
					 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					// 控制速度
 | 
										// 控制模拟速度
 | 
				
			||||||
					speedMultiplierMutex.RLock()
 | 
										speedMultiplierMutex.RLock()
 | 
				
			||||||
					currentMultiplier := speedMultiplier
 | 
										currentMultiplier := speedMultiplier
 | 
				
			||||||
					speedMultiplierMutex.RUnlock()
 | 
										speedMultiplierMutex.RUnlock()
 | 
				
			||||||
@ -1088,16 +1320,17 @@ func startRoute(stopChan chan struct{}, mqttClient mqtt.Client, route []RoutePoi
 | 
				
			|||||||
				MQTTClient:      mqttClient,
 | 
									MQTTClient:      mqttClient,
 | 
				
			||||||
				LoadWeight:      loadWeight,
 | 
									LoadWeight:      loadWeight,
 | 
				
			||||||
				RouteName:       routeName,
 | 
									RouteName:       routeName,
 | 
				
			||||||
				CoalPickupOrder: coalPickupOrder, // 固定提煤单
 | 
									CoalPickupOrder: coalPickupOrder,
 | 
				
			||||||
				Waybill:         "",              // 将在 runSimulation 中生成
 | 
									Waybill:         "",
 | 
				
			||||||
				DriverName:      driverName,      // 分配司机姓名
 | 
									DriverName:      driverName,
 | 
				
			||||||
				PhoneNumber:     "18940028888",   // 分配固定电话号码
 | 
									PhoneNumber:     "18940028888",
 | 
				
			||||||
				CarPlate:        "蒙K123ER",      // 新增字段,固定或动态分配
 | 
									CarPlate:        "蒙K123ER",
 | 
				
			||||||
				Mileage:         100.0,           // 新增字段,示例值,实际应动态获取
 | 
									Mileage:         100.0,
 | 
				
			||||||
				Speed:           60.0,            // 新增字段,示例值,实际应动态获取
 | 
									Speed:           60.0,
 | 
				
			||||||
				Location:        "北京",          // 新增字段,示例值,实际应动态获取
 | 
									Location:        "北京",
 | 
				
			||||||
				VehicleStatus:   "ACC正常",       // 新增字段,示例值,实际应动态获取
 | 
									VehicleStatus:   "ACC正常",
 | 
				
			||||||
				eventsSent:      make(map[int]bool),
 | 
									eventsSent:      make(map[int]bool),
 | 
				
			||||||
 | 
									triggeredEvents: make(map[string]bool), // 初始化触发事件记录
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			simulationWaitGroup.Add(1)
 | 
								simulationWaitGroup.Add(1)
 | 
				
			||||||
			go truck.runSimulation(stopChan, mainOrderNumber, coalPickupOrder)
 | 
								go truck.runSimulation(stopChan, mainOrderNumber, coalPickupOrder)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user