将 StandardScene 各插件的配置/监控窗体从 WinForms 迁移到 CycleGUI(删除 .Designer.cs/.resx,重写为 PanelBuilder 立即模式 UI,新增 CycleUiHelper 统一对话框)。 同时修复代码审核中的问题: - 后台文件写入加锁 + try/catch(ButtonBoxManager / DoorManager,对齐 LoopViewer.SaveTasks 模式) - CoderFieldsMetadata.cs 启用 #nullable enable,消除 CS8632 警告 - DummyCar 移除已废弃的 rightClickAction()/SetPosition() - CarRemoteHelper.OpenVehicleWebPage 的 Process.Start 加 try/catch - 重命名名不副实的 Mstsc()(现为打开网页) - 统一弃元命名为 _ - TrafficInterlockViewer 改用稳定 Id(GUID)做选择/编辑,替代行索引 - csproj 改用 $(CGUILibDir) 解析 CycleGUI,绝对路径收敛到 Directory.Build.props 构建:dotnet build StandardScene.sln → 0 错误,30 警告(均为历史遗留)。 注:static 单例状态重构(审核第 8 项)暂未处理,留待单独任务。
241 lines
10 KiB
C#
241 lines
10 KiB
C#
using SimpleCore.PropType;
|
|
using SimpleCore;
|
|
using System.Collections.Generic;
|
|
using System.Dynamic;
|
|
using SimpleLite.CADTools;
|
|
using SimpleLite.Props;
|
|
using SimpleLite.UI;
|
|
using System.Numerics;
|
|
using System.Drawing;
|
|
using System;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using SimpleLite.RCS;
|
|
using SimpleLite.RCS.CarTypes;
|
|
|
|
namespace StandardScene.Model
|
|
{
|
|
public class Map
|
|
{
|
|
public List<ExpandoObject> Sites { get; set; }
|
|
public List<ExpandoObject> Tracks { get; set; }
|
|
public List<ExpandoObject> CircularArcTracks { get; set; }
|
|
public List<ExpandoObject> BezierTracks { get; set; }
|
|
|
|
public static Map GetMap()
|
|
{
|
|
var map = new Map
|
|
{
|
|
Sites = new List<ExpandoObject>()
|
|
};
|
|
Site[] allSites = SimpleLib.GetAllSites();
|
|
foreach (var site in allSites)
|
|
{
|
|
dynamic theSite = new ExpandoObject();
|
|
theSite.Id = ((Prop)site).id;
|
|
theSite.Name = ((Prop)site).name;
|
|
theSite.X = site.x;
|
|
theSite.Y = site.y;
|
|
theSite.Fields = new Dictionary<string, string>();
|
|
foreach (var kv in site.fields)
|
|
{
|
|
theSite.Fields.Add(kv.Key, kv.Value);
|
|
}
|
|
theSite.Type = theSite.Fields.ContainsKey("Standby") ? "standby" : theSite.Fields.ContainsKey("Charge") ? "charge" : theSite.Fields.ContainsKey("Shelf") ? "shelf" : "default";
|
|
map.Sites.Add(theSite);
|
|
}
|
|
|
|
map.Tracks = new List<ExpandoObject>();
|
|
map.CircularArcTracks = new List<ExpandoObject>();
|
|
map.BezierTracks = new List<ExpandoObject>();
|
|
var allTracks = SimpleLib.GetAllTracks();
|
|
foreach (var track in allTracks)
|
|
{
|
|
switch (track)
|
|
{
|
|
case UITrack uiTrack:
|
|
{
|
|
dynamic theTrack = new ExpandoObject();
|
|
theTrack.Id = uiTrack.id;
|
|
theTrack.A = uiTrack.siteA;
|
|
theTrack.B = uiTrack.siteB;
|
|
theTrack.Fields = new Dictionary<string, string>();
|
|
foreach (KeyValuePair<string, string> kv in uiTrack.fields)
|
|
{
|
|
theTrack.Fields.Add(kv.Key, kv.Value);
|
|
}
|
|
|
|
theTrack.Direction = uiTrack.direction switch
|
|
{
|
|
1 => "a2b",
|
|
2 => "b2a",
|
|
_ => "both"
|
|
};
|
|
theTrack.Type = 0;
|
|
map.Tracks.Add(theTrack);
|
|
break;
|
|
}
|
|
case UICircularArcTrack circularArcTrack:
|
|
{
|
|
dynamic theTrack = new ExpandoObject();
|
|
theTrack.Id = circularArcTrack.id;
|
|
theTrack.A = circularArcTrack.siteA;
|
|
theTrack.B = circularArcTrack.siteB;
|
|
theTrack.Fields = new Dictionary<string, string>();
|
|
foreach (var kv in circularArcTrack.fields)
|
|
{
|
|
theTrack.Fields.Add(kv.Key, kv.Value);
|
|
}
|
|
|
|
theTrack.Direction = circularArcTrack.direction switch
|
|
{
|
|
1 => "a2b",
|
|
2 => "b2a",
|
|
_ => "both"
|
|
};
|
|
//添加原点坐标、半径、开始角度、结束角度
|
|
theTrack.Type = 1;
|
|
theTrack.CenterX = circularArcTrack.Arc.Center.X;
|
|
theTrack.CenterY = circularArcTrack.Arc.Center.Y;
|
|
theTrack.Radius = circularArcTrack.Arc.Radius;
|
|
theTrack.AngleStart = circularArcTrack.Arc.AngleStart;
|
|
theTrack.AngleEnd = circularArcTrack.Arc.AngleEnd;
|
|
//获取圆弧的控制点
|
|
var controlPoint = ArcHelper.CalculateControlPoint(
|
|
circularArcTrack.Arc.PointStart.X, circularArcTrack.Arc.PointStart.Y,
|
|
circularArcTrack.Arc.PointEnd.X, circularArcTrack.Arc.PointEnd.Y,
|
|
circularArcTrack.Arc.Center.X, circularArcTrack.Arc.Center.Y,
|
|
circularArcTrack.Arc.Radius,
|
|
circularArcTrack.Arc.AngleStart, circularArcTrack.Arc.AngleEnd);
|
|
theTrack.ControlPointsX = controlPoint.X;
|
|
theTrack.ControlPointsY = controlPoint.Y;
|
|
map.CircularArcTracks.Add(theTrack);
|
|
break;
|
|
}
|
|
case UIBezierTrack bezierTrack:
|
|
{
|
|
dynamic theTrack = new ExpandoObject();
|
|
theTrack.Id = bezierTrack.id;
|
|
theTrack.A = bezierTrack.siteA;
|
|
theTrack.B = bezierTrack.siteB;
|
|
theTrack.Fields = new Dictionary<string, string>();
|
|
foreach (var kv in bezierTrack.fields)
|
|
{
|
|
theTrack.Fields.Add(kv.Key, kv.Value);
|
|
}
|
|
|
|
theTrack.Direction = bezierTrack.direction switch
|
|
{
|
|
1 => "a2b",
|
|
2 => "b2a",
|
|
_ => "both"
|
|
};
|
|
//根据typeInfo获取控制点个数
|
|
theTrack.Type = 2;
|
|
var array = bezierTrack.typeInfo.Split(',');
|
|
var num = int.Parse(array[1]);//获取控制点的个数(包括起点和终点)
|
|
var list = new List<Vector2>();
|
|
for (var i = 0; i < num; i++)
|
|
{
|
|
//只记录中间的控制点
|
|
if (i <= 0 || i >= num - 1) continue;
|
|
float x = float.Parse(array[2 + i * 2]);
|
|
float y = float.Parse(array[3 + i * 2]);
|
|
list.Add(new Vector2(x, y));
|
|
}
|
|
theTrack.controlPoints = list;
|
|
map.BezierTracks.Add(theTrack);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return map;
|
|
}
|
|
}
|
|
|
|
public class ArcHelper
|
|
{
|
|
/// <summary>
|
|
/// 获取圆弧控制点
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static PointF CalculateControlPoint(float pointStartX, float pointStartY, float pointEndX, float pointEndY, float centerX, float centerY, float radius, float angleStart, float angleEnd)
|
|
{
|
|
PointF center = new PointF(centerX, centerY);
|
|
double startAngle = angleStart * 3.14159 / 180.0;
|
|
double endAngel = angleEnd * 3.14159 / 180.0;
|
|
if (angleStart > angleEnd)//如果起始角度小于结束角度就加上360度
|
|
{
|
|
endAngel += 2 * 3.14159;
|
|
}
|
|
PointF midPoint = new PointF(
|
|
(float)(center.X + radius * Math.Cos((startAngle + endAngel) / 2)),
|
|
(float)(center.Y + radius * Math.Sin((startAngle + endAngel) / 2)));
|
|
return midPoint;
|
|
}
|
|
}
|
|
|
|
public class LidarMap
|
|
{
|
|
public string LidarMapsBase64 { get; set; }
|
|
public float DistanceX { get; set; }
|
|
public float DistanceY { get; set; }
|
|
public float Ratio { get; set; }
|
|
private static readonly HttpClient hc = new HttpClient();
|
|
public static async Task<LidarMap> GetLidarMap()
|
|
{
|
|
var par = JsonConvert.DeserializeAnonymousType(await hc.GetStringAsync($"http://127.0.0.1:4321/getMapParameters"),
|
|
new { up = 0f, down = 0f, left = 0f, right = 0f, pt = 0 });
|
|
var lengthWidthRatio =Math.Abs((par.right - par.left) / (par.up - par.down));
|
|
var height = 1024;var width = 1024;
|
|
if (lengthWidthRatio > 1)
|
|
{
|
|
height = (int)(1024 / lengthWidthRatio);
|
|
}
|
|
else
|
|
{
|
|
width = (int)(1024 * lengthWidthRatio);
|
|
}
|
|
var mapBmp= new Bitmap((await hc.GetStreamAsync($"http://127.0.0.1:4321/getMapPng?width={width}&height={height}")));
|
|
for(int x =0;x<mapBmp.Width;x++)
|
|
{
|
|
for (int y = 0; y < mapBmp.Height; y++)
|
|
{
|
|
Color pixColor = mapBmp.GetPixel(x, y);
|
|
if (pixColor.R != 255 || pixColor.G != 255 || pixColor.B != 255)
|
|
{
|
|
mapBmp.SetPixel(x, y, Color.FromArgb(0,pixColor));
|
|
}
|
|
else
|
|
{
|
|
mapBmp.SetPixel(x,y,Color.Black);
|
|
}
|
|
}
|
|
}
|
|
return new LidarMap
|
|
{
|
|
LidarMapsBase64 = "data:image/png;base64," + BitmapToBase64(mapBmp, ImageFormat.Png),
|
|
DistanceX = par.left,
|
|
DistanceY = par.up,
|
|
Ratio = lengthWidthRatio > 1?Math.Abs(par.left-par.right)/1024: Math.Abs(par.up - par.down)/1024,
|
|
};
|
|
}
|
|
|
|
static string BitmapToBase64(Bitmap bmp, ImageFormat format)
|
|
{
|
|
using MemoryStream ms = new MemoryStream();
|
|
bmp.Save(ms, format); // 保存格式可以是Png, Jpeg等
|
|
byte[] imageBytes = ms.ToArray();
|
|
string base64String = Convert.ToBase64String(imageBytes);
|
|
return base64String;
|
|
}
|
|
}
|
|
}
|