MainWindow.xaml.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.IO;
  8. using System.IO.Compression;
  9. using System.IO.Ports;
  10. using System.Linq;
  11. using System.Text;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. using System.Windows;
  15. using System.Windows.Controls;
  16. using System.Windows.Data;
  17. using System.Windows.Documents;
  18. using System.Windows.Input;
  19. using System.Windows.Interop;
  20. using System.Windows.Media;
  21. using System.Windows.Media.Imaging;
  22. using System.Windows.Navigation;
  23. using System.Windows.Shapes;
  24. using Brush = System.Drawing.Brush;
  25. using Color = System.Drawing.Color;
  26. using Pen = System.Drawing.Pen;
  27. using PixelFormat = System.Drawing.Imaging.PixelFormat;
  28. using Rectangle = System.Drawing.Rectangle;
  29. namespace Air105Camera
  30. {
  31. /// <summary>
  32. /// MainWindow.xaml 的交互逻辑
  33. /// </summary>
  34. [PropertyChanged.AddINotifyPropertyChangedInterface]
  35. public partial class MainWindow : Window
  36. {
  37. public MainWindow()
  38. {
  39. InitializeComponent();
  40. }
  41. //将Bitmap对象转换成bitmapImage对象
  42. private BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
  43. {
  44. MemoryStream stream = new MemoryStream();
  45. bitmap.Save(stream, ImageFormat.Bmp);
  46. BitmapImage image = new BitmapImage();
  47. image.BeginInit();
  48. image.StreamSource = stream;
  49. image.EndInit();
  50. return image;
  51. }
  52. //刷新显示当前图片
  53. private void ShowImage()
  54. {
  55. this.Dispatcher.Invoke(() =>
  56. {
  57. var temp = ConvertBitmapToBitmapImage(image);
  58. CameraImage.Source = temp;
  59. });
  60. }
  61. //缓存图片数据
  62. Bitmap image = new Bitmap(1, 1, PixelFormat.Format16bppRgb565);
  63. //串口对象
  64. private SerialPort Uart = new SerialPort();
  65. //总共收到多少包
  66. public int totalPack { get; set; } = 0;
  67. //总共刷了多少张
  68. public int totalPic { get; set; } = 0;
  69. //FPS数
  70. public double fpsNow { get; set; } = 0;
  71. public bool connectEnable { get; set; } = true;
  72. public bool disconnectEnable { get; set; } = true;
  73. //当前宽高,给ui显示用
  74. public int imageWidth { get; set; } = 0;
  75. public int imageHeight { get; set; } = 0;
  76. private void Window_Loaded(object sender, RoutedEventArgs e)
  77. {
  78. this.DataContext = this;
  79. //图片显示一下
  80. ShowImage();
  81. // 绑定事件监听,用于监听HID设备插拔
  82. (PresentationSource.FromVisual(this) as HwndSource)?.AddHook(WndProc);
  83. RefreshPortList();
  84. //串口数据处理,随便写写
  85. new Thread(Uart2Image).Start();
  86. }
  87. private void ConnectButton_Click(object sender, RoutedEventArgs e)
  88. {
  89. if (Uart.IsOpen)
  90. {
  91. return;
  92. }
  93. if (((string)SerialComboBox.SelectedItem).Length > 0)
  94. {
  95. var portName = (string)SerialComboBox.SelectedItem;
  96. Task.Run(() =>
  97. {
  98. try
  99. {
  100. Uart.PortName = portName;
  101. Uart.Open();
  102. }
  103. catch (Exception ex)
  104. {
  105. MessageBox.Show(ex.Message);
  106. }
  107. });
  108. }
  109. }
  110. private void DisconnectButton_Click(object sender, RoutedEventArgs e)
  111. {
  112. if (!Uart.IsOpen)
  113. {
  114. return;
  115. }
  116. try
  117. {
  118. Uart.Close();
  119. }
  120. catch(Exception ex)
  121. {
  122. MessageBox.Show(ex.Message);
  123. }
  124. }
  125. private static int UsbPluginDeley = 0;
  126. private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
  127. {
  128. if (msg == 0x219)// 监听USB设备插拔消息
  129. {
  130. if (UsbPluginDeley == 0)
  131. {
  132. ++UsbPluginDeley; // Task启动需要准备时间,这里提前对公共变量加一
  133. Task.Run(() =>
  134. {
  135. do Task.Delay(100).Wait();
  136. while (++UsbPluginDeley < 10);
  137. UsbPluginDeley = 0;
  138. RefreshPortList();
  139. });
  140. }
  141. else UsbPluginDeley = 1;
  142. handled = true;
  143. }
  144. return IntPtr.Zero;
  145. }
  146. /// <summary>
  147. /// 刷新设备列表,并在断开恢复后重连
  148. /// </summary>
  149. /// <returns>串口列表是否成功获取</returns>
  150. private bool RefreshPortList()
  151. {
  152. bool result = true;
  153. var list = SerialPort.GetPortNames();
  154. Dispatcher.Invoke(delegate
  155. {
  156. SerialComboBox.ItemsSource = list;
  157. if (SerialComboBox.Items.Count == 0)
  158. {
  159. result = false;
  160. }
  161. else if (SerialComboBox.Items.Contains(Uart.PortName))
  162. {
  163. SerialComboBox.SelectedItem = Uart.PortName;
  164. }
  165. else SerialComboBox.SelectedIndex = 0;
  166. });
  167. return result;
  168. }
  169. bool closed = false;
  170. private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
  171. {
  172. closed = true;
  173. //直接强关软件
  174. Environment.Exit(0);
  175. }
  176. private void RefreshButton_Click(object sender, RoutedEventArgs e)
  177. {
  178. RefreshPortList();
  179. }
  180. /*
  181. 16字节头+N字节数据
  182. 4字节字符串 “VCAM” + 2字节图像宽度 + 2字节图像高度 + 4字节本次数据从哪行开始刷 + 2字节未压缩数据长度 + 2字节压缩后图像数据长度
  183. 如果压缩前和压缩后数据长度是一致的,那就没有压缩;如果不一致,则后面的N字节要用zlib解压
  184. RGB565大端格式
  185. */
  186. //串口数据处理
  187. private void Uart2Image()
  188. {
  189. byte[] temp = new byte[10 * 1024 * 1024];//缓冲区
  190. int p = 0;//上次的位置
  191. Stopwatch sw = new Stopwatch();
  192. sw.Start();
  193. bool getNewData = false;//记一下是不是收到新数据了
  194. while (true)
  195. {
  196. if (closed)
  197. return;
  198. connectEnable = !Uart.IsOpen;
  199. disconnectEnable = Uart.IsOpen;
  200. try
  201. {
  202. if (Uart.IsOpen && Uart.BytesToRead > 0)
  203. {
  204. //读数据
  205. var toread = Uart.BytesToRead;
  206. Uart.Read(temp, p, toread);
  207. p += toread;
  208. getNewData = true;
  209. }
  210. else if (!Uart.IsOpen)
  211. {
  212. fpsNow = 0;
  213. p = 0;
  214. }
  215. if(getNewData)
  216. {
  217. getNewData = false;//收到新数据再刷
  218. for (int i = 3; i < p; i++)
  219. {
  220. //匹配VCAM头
  221. if (temp[i - 3] == 'V' && temp[i - 2] == 'C' && temp[i - 1] == 'A' && temp[i] == 'M'
  222. && p - i > 16)
  223. {
  224. var width = BitConverter.ToUInt16(temp, i + 1);
  225. var height = BitConverter.ToUInt16(temp, i + 3);
  226. var start = BitConverter.ToUInt32(temp, i + 5);
  227. var rawLen = BitConverter.ToUInt16(temp, i + 9);
  228. var comLen = BitConverter.ToUInt16(temp, i + 11);
  229. Debug.WriteLine($"found VCAM: {BitConverter.ToString(temp, i - 3, 16)}\r\n" +
  230. $"{width},{height},{start},{rawLen},{comLen},{i},{p}");
  231. //获取实际数据长度
  232. var realLength = comLen < rawLen ? comLen : rawLen;
  233. if (realLength <= p - i - 13)//一包数据够了
  234. {
  235. totalPack++;
  236. Debug.WriteLine($"{realLength} bytes, start unpack");
  237. //存储rgb数据
  238. byte[] data = null;
  239. if (comLen < rawLen)//包压缩了
  240. {
  241. //Debug.WriteLine(BitConverter.ToString(temp, i + 13, comLen));
  242. using MemoryStream compressed = new MemoryStream(temp, i + 13, comLen);
  243. using MemoryStream decompressed = new MemoryStream();
  244. using InflaterInputStream inputStream = new InflaterInputStream(compressed);
  245. inputStream.CopyTo(decompressed);
  246. data = decompressed.ToArray();
  247. }
  248. else//没压缩
  249. {
  250. Debug.WriteLine($"rawLen,{rawLen},{i + 13 + rawLen}");
  251. data = new byte[rawLen];
  252. for (int n = i + 13; n < i + 13 + rawLen; n++)
  253. data[n - (i + 13)] = temp[n];
  254. }
  255. //自适应当前图片大小
  256. if(image.Width != width || image.Height != height)
  257. {
  258. image = new Bitmap(width, height, PixelFormat.Format16bppRgb565);
  259. (imageWidth, imageHeight) = (width, height);
  260. }
  261. //直接把rgb565数据写入bitmap,这样快
  262. //先把数据改成小端
  263. for (int n = 0; n < data.Length; n += 2)
  264. {
  265. (data[n], data[n + 1]) = (data[n + 1], data[n]);
  266. }
  267. var rect = new Rectangle(0, (int)start, width, data.Length / 2 / width);
  268. BitmapData bmpData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);
  269. IntPtr ptr = bmpData.Scan0;// Get the address of the first line.
  270. System.Runtime.InteropServices.Marshal.Copy(data, 0, ptr, data.Length);
  271. image.UnlockBits(bmpData);
  272. //刷到最后一行时再显示当前图片
  273. if (start >= height - 16)
  274. {
  275. sw.Stop();
  276. fpsNow = 1000.0 / sw.ElapsedMilliseconds;
  277. sw.Restart();
  278. totalPic++;
  279. ShowImage();
  280. }
  281. //剩下的数据全部左移到开头
  282. for (int n = i + 13 + realLength; n < p; n++)
  283. temp[n - (i + 13 + realLength)] = temp[n];
  284. p -= i + 13 + realLength;
  285. Debug.WriteLine($"done, p now {p}");
  286. if (p > 3)
  287. i = 2;
  288. else
  289. break;
  290. }
  291. }
  292. }
  293. }
  294. Thread.Sleep(1);
  295. }
  296. catch (Exception e)
  297. {
  298. Debug.WriteLine(e.Message);
  299. }
  300. }
  301. }
  302. }
  303. }