using System; using System.IO; using CartActivator; using CycleGUI; using CycleGUI.API; using Medulla.Types; namespace MultiWheelM; public partial class CartDefinition { private bool _fleetRemoteActive; private DateTime _fleetDiagLastStick = DateTime.MinValue; // 写盘诊断日志(Medulla 侧)。文件落在 Medulla 工作目录,便于复现后离线分析。 // 每次进程启动清空一次(_fleetDiagInit),同一次运行内追加,避免跨多次运行累积。 private static bool _fleetDiagInit; private void FleetDiag(string msg) { try { const string path = "fleet_diag_medulla.log"; var line = $"{DateTime.Now:HH:mm:ss.fff} car{CarNum} {msg}{Environment.NewLine}"; if (!_fleetDiagInit) { _fleetDiagInit = true; File.WriteAllText(path, $"=== session start {DateTime.Now:yyyy-MM-dd HH:mm:ss} ==={Environment.NewLine}" + line); } else File.AppendAllText(path, line); } catch { // 诊断日志失败不影响主流程 } } private void ZeroFleetManualFields() { MultiVehicleManualEnabled = false; MultiVehicleManualMode = 0; MultiVehicleManualVx = 0; MultiVehicleManualVy = 0; MultiVehicleManualVth = 0; } [IOObjectUtility] public void FleetRemote() { if (_fleetRemoteActive) { GUI.GetPanelInstancingObject(this)?.BringToFront(); return; } _fleetRemoteActive = true; var fleetOn = false; // 速度比例 0~1,作用于摇杆输出的线速度与转向角。 var speedRatio = 1f; UseGesture? manip = null; void CleanupFleetRemote() { manip?.End(); manip = null; fleetOn = false; ZeroFleetManualFields(); _fleetRemoteActive = false; } manip = new UseGesture(); manip.AddWidget(new UseGesture.ToggleWidget { name = "fleet_enable", text = "车队联动", position = "12.5%+10px, 75%+10px", size = "25%-10px, 12.5%-10px", OnValue = b => { fleetOn = b; MultiVehicleManualEnabled = b; MultiVehicleManualMode = 0; if (!b) ZeroFleetManualFields(); FleetDiag($"TOGGLE fleetOn={b} -> ManualEnabled={MultiVehicleManualEnabled}"); } }); manip.AddWidget(new UseGesture.ThrottleWidget { name = "fleet_speed_ratio", text = "速度比例", position = "50%+10px, 75%+10px", size = "50%-10px, 12.5%-10px", bounceBack = false, OnValue = (val, _) => speedRatio = Math.Clamp(val, 0, 1) }); manip.AddWidget(new UseGesture.ButtonWidget { name = "fleet_stop", text = "急停/归零", position = "25%+10px, 87.5%+10px", size = "25%-10px, 12.5%-10px", OnPressed = _ => { MultiVehicleManualVx = 0; MultiVehicleManualVy = 0; MultiVehicleManualVth = 0; FleetDiag("BUTTON stop/zero"); } }); manip.AddWidget(new UseGesture.StickWidget { name = "fleet_stick", text = "速度摇杆", position = "37.5%+10px, 25%+10px", size = "37.5%-10px, 37.5%-10px", bounceBack = true, keyboard = "Up,Down,Left,Right", joystick = "Axis0,Axis1", OnValue = (pos, manipulating) => { if (!fleetOn) { MultiVehicleManualVx = 0; MultiVehicleManualVy = 0; MultiVehicleManualVth = 0; if ((DateTime.Now - _fleetDiagLastStick).TotalMilliseconds >= 200) { _fleetDiagLastStick = DateTime.Now; FleetDiag($"STICK(ignored,fleetOff) pos=({pos.X:0.00},{pos.Y:0.00}) manip={manipulating}"); } return; } // pos.Y(前后) → 车队线速度(m/s);pos.X(左右) → 车队转向角(deg)。松手 bounceBack 自动回零。 var ratio = Math.Clamp(speedRatio, 0, 1); MultiVehicleManualVx = Math.Clamp(pos.Y, -1, 1) * MaxManualSpeed * ratio; MultiVehicleManualVy = 0; MultiVehicleManualVth = Math.Clamp(pos.X, -1, 1) * MaxManualAngularSpeed * ratio; if ((DateTime.Now - _fleetDiagLastStick).TotalMilliseconds >= 200) { _fleetDiagLastStick = DateTime.Now; FleetDiag($"STICK pos=({pos.X:0.00},{pos.Y:0.00}) manip={manipulating} ratio={ratio:0.00} " + $"maxV={MaxManualSpeed:0.00} maxW={MaxManualAngularSpeed:0.0} " + $"-> Vx={MultiVehicleManualVx:0.000} Vth={MultiVehicleManualVth:0.0} en={MultiVehicleManualEnabled}"); } } }); manip.ChangeState(new SetAppearance { drawGuizmo = false }); manip.Start(); GUI.PromptOrBringToFront(pb => { if (pb.Closing()) { CleanupFleetRemote(); pb.Panel.Exit(); return; } pb.Panel.TopMost(true) .SetDefaultDocking(Panel.Docking.None) .ShowTitle("车队联动遥控") .InitSize(320, 160) .InitPos(false, -32, 32, 1, 0, 1, 0); pb.SeparatorText("状态"); pb.Label($"车队联动: {MultiVehicleManualEnabled}"); pb.Label($"速度比例: {speedRatio:0.00}"); pb.Label($"Vx={MultiVehicleManualVx:0.000} m/s, Vth={MultiVehicleManualVth:0.0} deg/s"); pb.Label($"优先级: {CartActivator.CartDefinition.currentPriority} ({CartActivator.CartDefinition.currentPriorityDesc})"); pb.Label("请勿同时打开「手动控制」面板"); pb.Panel.Repaint(); }, instancingObject: this); } }