视展LED扩展卡局部刷新

任务队列

代码来自https://github.com/CuteLeon/TaskQueueDemo

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LEDManager
{
    /// <summary>
    /// 单元任务
    /// </summary>
    public abstract class UnitTask
    {
        /// <summary>
        /// 任务名称
        /// </summary>
        public string Name { get; set; } = string.Empty;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        public UnitTask(string name) => this.Name = name;
        public override string ToString() => $"任务:{this.Name}";

        /// <summary>
        /// 执行任务
        /// </summary>
        public abstract void Execute();
    }

    /// <summary>
    /// 任务队列
    /// </summary>
    public class TaskQueue<T> : IDisposable where T : UnitTask
    {
        #region 事件

        /// <summary>
        /// 有任务入队事件
        /// </summary>
        public event EventHandler<T> TaskEnqueued;// { add { } remove { } }

        /// <summary>
        /// 有任务出队事件
        /// </summary>
        public event EventHandler TaskDequeued;// { add { } remove { } }

        /// <summary>
        /// 队列开始执行
        /// </summary>
        public event DoWorkEventHandler QueueStarted;// { add { } remove { } }

        /// <summary>
        /// 队列停止执行
        /// </summary>
        public event RunWorkerCompletedEventHandler QueueStoped;// { add { } remove { } }

        /// <summary>
        /// 队列进入空闲状态
        /// </summary>
        public event EventHandler Idle;// { add { } remove { } }
        #endregion

        #region 属性

        /// <summary>
        /// 任务队列名称
        /// </summary>
        public string Name { get; set; } = string.Empty;
        public IntPtr handle { get; set; }

        /// <summary>
        /// 任务列表(线程安全)
        /// </summary>
        private ConcurrentQueue<T> Tasks { get; set; } = new ConcurrentQueue<T>();

        /// <summary>
        /// 任务列表(只读)
        /// </summary>
        public T[] ReadOnlyTasks { get => this.Tasks.ToArray(); }

        /// <summary>
        /// 队列内任务总数
        /// </summary>
        public int TaskCount { get => this.Tasks?.Count() ?? 0; }
        #endregion

        #region 变量

        /// <summary>
        /// 任务执行线程
        /// </summary>
        private readonly BackgroundWorker TaskWorker = new BackgroundWorker()
        {
            WorkerReportsProgress = false,
            WorkerSupportsCancellation = true
        };

        /// <summary>
        /// 任务控制信号量(防止队列循环空转)
        /// </summary>
        private readonly ManualResetEvent QueueEvent = new ManualResetEvent(false);
        #endregion

        public TaskQueue(string name)
        {
            this.handle = handle;
            this.Name = name;

            this.TaskWorker.DoWork += this.ExecuteTasks;
            this.TaskWorker.RunWorkerCompleted += this.ExecuteFinished;
        }

        /// <summary>
        /// 任务入队
        /// </summary>
        /// <param name="task"></param>
        public void Enqueue(T task)
        {
            if (task == null) return;

            if (this.TaskCount == 0 && this.TaskWorker.IsBusy)
            {
                Console.WriteLine($"<{this.Name}> 队列信号量 Enqueue-Set()");
                this.QueueEvent.Set();
            }
            this.Tasks.Enqueue(task);

            TaskEnqueued?.Invoke(this, task);
        }

        /// <summary>
        /// 任务出队
        /// </summary>
        /// <returns></returns>
        public T Dequeue()
        {
            bool result = this.Tasks.TryDequeue(out T task);
            if (result) TaskDequeued?.Invoke(this, null);
            return task;
        }

        /// <summary>
        /// 任务队列开始执行
        /// </summary>
        public void Start()
        {
            if (this.TaskWorker.IsBusy) return;
            this.TaskWorker.RunWorkerAsync();
        }

        /// <summary>
        /// 任务队列开始执行
        /// </summary>
        public void Start(object argument)
        {
            if (this.TaskWorker.IsBusy) return;
            this.TaskWorker.RunWorkerAsync(argument);
        }

        /// <summary>
        /// 任务队列停止执行
        /// </summary>
        public void Stop()
        {
            if (!this.TaskWorker.IsBusy) return;
            this.TaskWorker.CancelAsync();
            Console.WriteLine($"<{this.Name}> 队列信号量 Stop-Set()");
            this.QueueEvent.Set();
        }

        /// <summary>
        /// 开始轮询执行任务
        /// </summary>
        private void ExecuteTasks(object sender, DoWorkEventArgs e)
        {
            QueueStarted?.Invoke(this, e);
            Console.WriteLine($"<{this.Name}> 内 Worker 启动...");

            while (true)
            {
                try
                {
                    //Thread.Sleep(1000);
                    Console.WriteLine($"<{this.Name}> 队列内任务数:{this.TaskCount}");

                    if ((sender as BackgroundWorker).CancellationPending) return;
                    if (this.TaskCount == 0)
                    {
                        Console.WriteLine($"<{this.Name}> 队列信号量 Execute-WaitOne");
                        // 阻塞队列状态
                        this.QueueEvent.Reset();
                        this.QueueEvent.WaitOne();

                        //队列进入空闲状态,触发空闲事件
                        Idle?.Invoke(this, null);
                        //WaitOne 之后要先 continue 一次
                        continue;
                    }

                    T task = this.Dequeue();
                    if (task == null) continue;

                    task.Execute();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"<{this.Name}> 队列内发生异常:{ex.Message}");
                }
            }
        }

        /// <summary>
        /// 任务轮询结束
        /// </summary>
        private void ExecuteFinished(object sender, RunWorkerCompletedEventArgs e)
        {
            Console.WriteLine($"<{this.Name}> 内 Worker 停止...");
            Console.WriteLine($"<{this.Name}> 队列内剩余任务数:{this.TaskCount}");

            if (e.Error != null) Console.WriteLine($"<{this.Name}> 队列内发生异常:{e.Error.Message}");
            QueueStoped?.Invoke(this, e);
        }

        #region IDisposable Support

        public void Dispose()
        {
            this.Stop();
            this.TaskWorker.Dispose();
            while (this.Tasks.TryDequeue(out T task)) { }
            this.Tasks = null;
            this.QueueEvent.Close();
            this.QueueEvent.Dispose();

            GC.SuppressFinalize(this);
        }
        #endregion
    }
}

自己写一个任务

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System;
using System.Collections.Generic;


namespace LEDManager
{
    public class LEDSendTask:UnitTask
    {
        private WindowInfo _wi;
        public LEDSendTask(string name, WindowInfo wi) : base(name) {
            _wi = wi;
        }
        public static CLEDSender LEDSender = new CLEDSender();
        public const int WM_LED_NOTIFY = 1025;
        private const int GREEN = 0xFF00;
        private const int YELLOW = 0x00FFFF;
        private const int RED = 0x0000FF;
        private static TSenderParam GetDevParam(string remoteIP,IntPtr handle, ushort imei=0)
        {
            TSenderParam param = new TSenderParam();
            param.devParam.devType = LEDSender.DEVICE_TYPE_UDP;
            param.devParam.comPort = 1;
            param.devParam.comSpeed = 0;
            param.devParam.locPort = 8881;
            param.devParam.rmtHost = remoteIP;
            param.devParam.rmtPort = 6666;
            param.devParam.dstAddr = imei;
            param.notifyMode = LEDSender.NOTIFY_BLOCK;
            param.wmHandle = (uint)handle;
            param.wmMessage = WM_LED_NOTIFY;
            return param;
        }
        private static int GetColor(int color)
        {
            switch (color)
            {
                case 1: return RED;
                case 2: return YELLOW;
                case 3: return GREEN;
                default:
                    return RED;
            }
        }
        private string Parse(Int32 sendResult)
        {
            if (sendResult == LEDSender.R_DEVICE_READY) return "正在执行命令或者发送数据...";
            else if (sendResult == LEDSender.R_DEVICE_INVALID) return "打开通讯设备失败(串口不存在、或者串口已被占用、或者网络端口被占用)";
            else if (sendResult == LEDSender.R_DEVICE_BUSY) return "设备忙,正在通讯中...";
            else return "无";
        }

        public override void Execute()
        {
            WinMessage.Send(_wi.handle, WinMessage.WM_LOG_MESSAGE, $"执行任务:{Name}");

            WinMessage.Send(_wi.handle, WinMessage.WM_LOG_MESSAGE, $"发送: {_wi.RemoteIP} 硬件地址: {_wi.IMEI}");

            foreach (var item in _wi.ObjList)
            {
                var K = (ushort)LEDSender.Do_MakeObject(CLEDSender.ROOT_PLAY_OBJECT, CLEDSender.ACTMODE_REPLACE, 0, 0, 0, item.ObjIndex, CLEDSender.COLOR_MODE_DOUBLE);
                var d = GetDevParam(_wi.RemoteIP,_wi.handle,0);
                if (item.IsScroll)
                {
                    LEDSender.Do_AddText(K,
                    item.Left, item.Top, item.Width, item.Height, //对象位置
                    CLEDSender.V_TRUE,//透明 
                    0, //边框
                    item.Text, //内容 
                    item.FontName, item.FontSize, GetColor(item.FontColor), item.Bold ? CLEDSender.WFS_BOLD : CLEDSender.WFS_NONE, //字体样式
                    0, //换行 
                    item.Alignment,//对齐
                    2, 5, // 进入动画 6 连续左滚 2 左滚
                    2, 5, // 退出动画
                    0, 5, 0 // 停留动画
                    );
                }
                else
                {
                    LEDSender.Do_AddText(K,
                                        item.Left, item.Top, item.Width, item.Height,//对象位置
                                        CLEDSender.V_TRUE, //透明 
                                        0,//边框
                                        item.Text, //内容 
                                        item.FontName, item.FontSize, GetColor(item.FontColor), item.Bold ? CLEDSender.WFS_BOLD : CLEDSender.WFS_NONE, //字体样式
                                        0,  //换行 
                                        item.Alignment//对齐
                                        );
                }

               var sendResult = LEDSender.Do_LED_SendToScreen(ref d, K);
                
                //WinMessage.Send(_wi.handle, WinMessage.WM_LOG_MESSAGE, $"发送led:{sendResult} {Parse(sendResult)}");
            }
            WinMessage.Send(_wi.handle, WinMessage.WM_LOG_MESSAGE, $"任务完成:{Name}");
        }
    }
    public class WindowInfo
    {
        public IntPtr handle { get; set; }
        public string RemoteIP { get; set; }
        public int IMEI { get; set; }
        public List<WindowObj2> ObjList { get; set; }
    }
    public class WindowObj2
    {
        public int Left { get; set; }
        public int Top { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public string Text { get; set; }
        public string FontName { get; set; }
        public int FontSize { get; set; }
        public bool Bold { get; set; }
        public int FontColor { get; set; }
        public bool IsScroll { get; set; }
        public int Alignment { get; set; }
        public int ObjIndex { get; set; }
        public int ObjType { get; set; }
    }
}
  • 由于使用了任务队列,则无需异步发送,直接使用同步发送
1
2
3
param.notifyMode = LEDSender.NOTIFY_BLOCK;
param.wmHandle = (uint)handle;
param.wmMessage = WM_LED_NOTIFY;

wmHandle是必须的,可能dll在发送时需要句柄

  • Do_LED_SendToScreen的第一个参数是ref,就是说每次发送会变化, 所以TSenderParam 每次发送都需要创建,否则在多次连续发送时会失败

  • 这里直接使用 Do_MakeObject 这种更新对象的方式来更新led, 好处是每次发送的内容只是一小块区域。比如排队叫号里一个窗口一般由 窗口号 窗口名 交互区 三个对象组成,那么当我需要更新一个窗口的信息时,只要管三个对象即可。而如果使用节目的方式,则每次更新都要把整个led上的数据全部发送一遍,效率明显会比较低。对于每一块扩展卡而言,ObjIndex 必须唯一,这是标记每个对象用的

Licensed under CC BY-NC-SA 4.0
记录平时瞎折腾遇到的各种问题, 方便查找
使用 Hugo 构建
主题 Stack 3.29.0Jimmy 设计