using GrpcClient.UdpService;
using System.Collections.Concurrent;
using ScottPlot.WinForms;
using ScottPlot;
namespace ImageControl
{
public partial class FormXXDate : Form
{
// 抓抓机管理
ConcurrentDictionary<XXserver, XXmachine> XXToChannelMap = null;
// 第一个抓抓机
XXmachine _machine;
private CircularHistory<WorldCoord> NegativePressureData { get; set; } = new CircularHistory<WorldCoord>(12); //数据缓存-负压数据源
private CircularHistory<WorldCoord> PumpSpeedData { get; set; } = new CircularHistory<WorldCoord>(12); //数据缓存-转速数据源
readonly FormsPlot formsPlot1 = new FormsPlot() { Dock = DockStyle.Fill }; //绘图控件
public FormXXDate(ConcurrentDictionary<XXserver, XXmachine> XXToChannelMap, CircularHistory<WorldCoord> negativePressureData, CircularHistory<WorldCoord> pumpSpeedData)
{
InitializeComponent();
this.XXToChannelMap = XXToChannelMap;
NegativePressureData = negativePressureData; //红箱数据源
PumpSpeedData = pumpSpeedData; //商品总数数据源
panel1.Controls.Add(formsPlot1); //添加绘图控件
ConfigureFormsPlot(); //配置绘图控件
PlotGraph(); //绘制图像
// 先不考虑有很多个抓抓机的情况,直接绑定第一个抓抓机
// foreach (var actionStatus in XXToChannelMap.Values)
_machine = XXToChannelMap.FirstOrDefault().Value;
if (_machine == null)
{
MessageBox.Show("抓抓机找不到服务机器!!!");
}
else
{
_machine.DeviceStatusUpdated += m =>
{
// 跨线程安全更新
if (InvokeRequired)
Invoke(() => UpdateGraphData(_machine.ActionStatus));
else
UpdateGraphData(_machine.ActionStatus);
};
}
// 初始化DataGridView
InitializeDataGridView();
}
private void InitializeDataGridView()
{
// 清空表格
dataGridView.Rows.Clear();
// 固定行数,与 XXToChannelMap 的键数一致
foreach (var key in XXToChannelMap)
{
dataGridView.Rows.Add();
}
}
private void UpdateMachineRow(XXmachine machine, int rowIndex)
{
// 获取指定行
var row = dataGridView.Rows[rowIndex];
// 更新行中的单元格
// UpdateRowIfChanged(row, 0, GetFlagsDescription(machine.SystemStatus.Flags)); // 系统标记位
// UpdateRowIfChanged(row, 1, machine.SystemStatus.X.ToString("F2")); // 坐标X(保留2位小数)
// UpdateRowIfChanged(row, 2, machine.SystemStatus.Y.ToString("F2")); // 坐标Y(保留2位小数)
// UpdateRowIfChanged(row, 3, machine.SystemStatus.Z.ToString("F2")); // 坐标Z(保留2位小数)
// UpdateRowIfChanged(row, 4, machine.SystemStatus.ArmPosition.ToString("F2")); // 长臂位置(保留2位小数)
// UpdateRowIfChanged(row, 5, machine.SystemStatus.ForearmPosition.ToString("F2")); // 短臂位置(保留2位小数)
// UpdateRowIfChanged(row, 6, machine.SystemStatus.ZAxisPosition.ToString("F2")); // Z轴位置(保留2位小数)
// UpdateRowIfChanged(row, 7, GetActionTypeDescription(machine.ActionStatus.Type)); // 动作类型(中文描述)
// UpdateRowIfChanged(row, 8, GetActionStateDescription(machine.ActionStatus.State)); // 动作状态(中文描述)
}
private void UpdateRowIfChanged(DataGridViewRow row, int cellIndex, object newValue)
{
var currentValue = row.Cells[cellIndex].Value;
if (!Equals(currentValue, newValue))
{
row.Cells[cellIndex].Value = newValue;
}
}
private void UpdateDataGridView()
{
// 暂停绘制,减少闪烁
dataGridView.SuspendLayout();
int rowIndex = 0;
foreach (var entry in XXToChannelMap)
{
var machine = entry.Value;
UpdateMachineRow(machine, rowIndex);
rowIndex++;
}
// 恢复绘制
dataGridView.ResumeLayout();
}
public void UpdateGraphData(ActionStatus actionStatus)
{
// 获取当前时间戳
long timestamp = DateTime.Now.Ticks;
// 单位pa
short np = actionStatus.NegativePressure;
// 转化单位Kpa (decimal)
decimal negativePressureKPa = np / 100m;
// 更新负压数据(Y轴:负压值,单位KPa)
NegativePressureData.Push(new WorldCoord(timestamp, negativePressureKPa));
// 更新转速数据(Y轴:转速百分比 0-100%)
PumpSpeedData.Push(new WorldCoord(timestamp, actionStatus.PumpSpeed));
}
private void timer1_Tick(object sender, EventArgs e)
{
// 定时刷新表格数据
UpdateDataGridView();
//刷新图表
PlotGraph();
}
private string GetFlagsDescription(ushort flags)
{
var descriptions = new List<string>();
if ((flags & (ushort)SystemFlag.UnderVoltage) != 0)
descriptions.Add("系统电源欠压");
if ((flags & (ushort)SystemFlag.Initializing) != 0)
descriptions.Add("正在初始化");
if ((flags & (ushort)SystemFlag.ArmMotorError) != 0)
descriptions.Add("长臂电机异常");
if ((flags & (ushort)SystemFlag.ForearmMotorError) != 0)
descriptions.Add("短臂电机异常");
if ((flags & (ushort)SystemFlag.ZAxisMotorError) != 0)
descriptions.Add("Z轴电机异常");
if ((flags & (ushort)SystemFlag.PumpError) != 0)
descriptions.Add("气泵异常");
if ((flags & (ushort)SystemFlag.PressureError) != 0)
descriptions.Add("气压检测异常");
if ((flags & (ushort)SystemFlag.CameraError) != 0)
descriptions.Add("相机异常");
if ((flags & (ushort)SystemFlag.ShutdownPosition) != 0)
descriptions.Add("关机位置");
if ((flags & (ushort)SystemFlag.StandbyPosition) != 0)
descriptions.Add("待机位置");
if ((flags & (ushort)SystemFlag.PreGraspPosition) != 0)
descriptions.Add("预抓取位置");
if ((flags & (ushort)SystemFlag.Executing) != 0)
descriptions.Add("指令执行中");
if ((flags & (ushort)SystemFlag.Ready) != 0)
descriptions.Add("系统就绪");
return descriptions.Count > 0 ? string.Join(",", descriptions) : "无标记";
}
//private string GetActionTypeDescription(ActionType type)
//{
// switch (type)
// {
// case ActionType.None:
// return "无动作";
// case ActionType.Initialize:
// return "初始化";
// case ActionType.Shutdown:
// return "关机移动";
// case ActionType.MoveTo3D:
// return "3D坐标移动";
// case ActionType.MoveToStandby:
// return "待机位置";
// case ActionType.MoveToPreCheck:
// return "预检位置";
// case ActionType.MoveToPick:
// return "去拣货";
// case ActionType.MoveToPostPick:
// return "拣货完成";
// case ActionType.MoveToDrop:
// return "去丢货";
// default:
// return "未知动作";
// }
//}
private string GetActionStateDescription(ActionState state)
{
switch (state)
{
case ActionState.None:
return "无状态";
case ActionState.Executing:
return "执行中";
case ActionState.Completed:
return "已完成";
default:
return "未知状态";
}
}
// 绘制图像
private void PlotGraph()
{
try
{
formsPlot1.Plot.Clear(); //清除旧图,一定要先清除,在更新。至关重要!!!!-要不然很影响性能
if (NegativePressureData.Length > 0) //负压
{
PlotData(NegativePressureData, Colors.Red, formsPlot1.Plot.Axes.Left); //更新Y轴最大值
}
if (PumpSpeedData.Length > 0) //转速
{
PlotData(PumpSpeedData, Colors.Black, formsPlot1.Plot.Axes.Right); //更新Y轴最大值
}
formsPlot1.Plot.Axes.AutoScale(); //设置Y 轴最舒服的大小
formsPlot1.Refresh(); //刷新图像
}
catch (Exception ex)
{
MessageBox.Show("负压/转速曲线图", ex.Message);
}
}
//private void PlotData(CircularHistory<WorldCoord> dataBuffer, ScottPlot.Color color, IYAxis yAxis)
//{
// var datas = dataBuffer.All.ToArray(); //获取所有数据
// double[] ys = datas.Select(point => (double)point.Y).ToArray(); //Y轴数据
// DateTime[] datesTime = datas.Select(t => new DateTime((long)t.X)).ToArray(); //将X轴数据转换为时间数组
// double[] xs = datesTime.Select(d => d.ToOADate()).ToArray(); // 创建 X 轴数据
// if (xs != null && ys != null && xs.Length > 0 && ys.Length > 0)
// {
// var signal = formsPlot1.Plot.Add.SignalXY(xs, ys, color); // 绘制原始数据
// signal.Axes.XAxis = formsPlot1.Plot.Axes.Bottom; // 使用底部 X 轴
// signal.Axes.YAxis = yAxis; // 使用指定的 Y 轴
// }
//}
private void PlotData(CircularHistory<WorldCoord> dataBuffer, ScottPlot.Color color, IYAxis yAxis)
{
var datas = dataBuffer.All.ToArray();
// 当前时间点
DateTime now = DateTime.Now;
// 只保留最近16秒的数据
var recentData = datas
.Select(point => new { Time = new DateTime((long)point.X), Y = point.Y })
.Where(p => (now - p.Time).TotalSeconds <= 16)
.ToArray();
if (recentData.Length > 0)
{
double[] xs = recentData.Select(p => p.Time.ToOADate()).ToArray();
double[] ys = recentData.Select(p => (double)p.Y).ToArray();
var signal = formsPlot1.Plot.Add.SignalXY(xs, ys, color);
signal.Axes.XAxis = formsPlot1.Plot.Axes.Bottom;
signal.Axes.YAxis = yAxis;
}
}
// 配置负压/转速曲线图基础配置
private void ConfigureFormsPlot()
{
formsPlot1.Plot.Grid.IsVisible = false; //隐藏网格
formsPlot1.Plot.Axes.Title.Label.FontName = Fonts.Detect("微软雅黑"); //设置标题字体
formsPlot1.Plot.Axes.Title.Label.FontSize = 25; //设置标题字体大小
formsPlot1.Plot.Axes.Bottom.Label.FontName = Fonts.Detect("微软雅黑"); //设置 X 轴字体
formsPlot1.Plot.Axes.Left.Label.FontName = Fonts.Detect("微软雅黑"); //设置 Y 轴字体
formsPlot1.Plot.Axes.Right.Label.FontName = Fonts.Detect("微软雅黑"); //设置 Y 轴字体
formsPlot1.Plot.Title("负压/转速曲线图"); //设置标题
formsPlot1.Plot.Axes.Left.Label.Text = "气泵负压"; //设置左Y轴标签
formsPlot1.Plot.Axes.Right.Label.Text = "转速"; //设置右Y轴标签
// 图例
LegendItem item1 = new LegendItem
{
LineColor = Colors.Red,
MarkerFillColor = Colors.Red,
MarkerLineColor = Colors.Red,
LineWidth = 4,
LabelText = "气泵负压(KPa)",
LabelFontName = Fonts.Detect("微软雅黑")
};
LegendItem item2 = new LegendItem
{
LabelText = "转速(%)",
LabelFontName = Fonts.Detect("微软雅黑"),
LineColor = Colors.Black,
MarkerFillColor = Colors.Black,
MarkerLineColor = Colors.Black,
LineWidth = 4,
};
LegendItem[] items = { item1, item2 };
formsPlot1.Plot.ShowLegend(items, location: Alignment.UpperCenter); //显示图例,居中在上方
formsPlot1.Plot.ShowLegend(edge: Edge.Top); //让图例显示到外面去
formsPlot1.Plot.Benchmark.IsVisible = false; //性能信息
formsPlot1.Plot.Axes.DateTimeTicksBottom(); // 设置 X 轴为时间轴---这里只能设置一次,如果设置多次会默认你是两条不同的曲线
}
public void ClearGraphData()
{
NegativePressureData.Clear();
PumpSpeedData.Clear();
formsPlot1.Plot.Clear();
formsPlot1.Refresh();
}
}
}
我想要将我的x轴,调整时间的间距,以ms为间距,