ソースを参照

add: usb摄像头显示上位机

chenxuuu 3 年 前
コミット
8e7bef2a16

+ 0 - 0
usb_camera_wpf/Air105摄像头预览.csproj → usb_camera_winform/Air105摄像头预览.csproj


+ 0 - 0
usb_camera_wpf/Air105摄像头预览.sln → usb_camera_winform/Air105摄像头预览.sln


+ 0 - 0
usb_camera_wpf/Form1.Designer.cs → usb_camera_winform/Form1.Designer.cs


+ 0 - 0
usb_camera_wpf/Form1.cs → usb_camera_winform/Form1.cs


+ 0 - 0
usb_camera_wpf/Form1.resx → usb_camera_winform/Form1.resx


+ 0 - 0
usb_camera_wpf/Program.cs → usb_camera_winform/Program.cs


+ 7 - 0
usb_camera_winform/README.md

@@ -0,0 +1,7 @@
+# Air105 USB虚拟串口摄像头的上位机程序
+
+C#  + WinForm + VS2022 , 水平有限, 就能用的级别
+
+需要配合 core-air105 和 camera_usb demo来使用
+
+

+ 25 - 0
usb_camera_wpf/Air105Camera.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32526.322
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Air105Camera", "Air105Camera\Air105Camera.csproj", "{FEB0E15C-EB94-43F0-89FA-417E7E736CD4}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{FEB0E15C-EB94-43F0-89FA-417E7E736CD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FEB0E15C-EB94-43F0-89FA-417E7E736CD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FEB0E15C-EB94-43F0-89FA-417E7E736CD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FEB0E15C-EB94-43F0-89FA-417E7E736CD4}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {4790DA40-B35D-47B1-889D-10F0770E24B1}
+	EndGlobalSection
+EndGlobal

+ 121 - 0
usb_camera_wpf/Air105Camera/Air105Camera.csproj

@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\PropertyChanged.Fody.3.4.1\build\PropertyChanged.Fody.props" Condition="Exists('..\packages\PropertyChanged.Fody.3.4.1\build\PropertyChanged.Fody.props')" />
+  <Import Project="..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{FEB0E15C-EB94-43F0-89FA-417E7E736CD4}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>Air105Camera</RootNamespace>
+    <AssemblyName>Air105Camera</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8.0</LangVersion>
+    <Deterministic>true</Deterministic>
+    <TargetFrameworkProfile />
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8.0</LangVersion>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8.0</LangVersion>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
+      <HintPath>..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll</HintPath>
+    </Reference>
+    <Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
+      <HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
+    </Reference>
+    <Reference Include="PropertyChanged, Version=3.4.1.0, Culture=neutral, PublicKeyToken=ee3ee20bcf148ddd, processorArchitecture=MSIL">
+      <HintPath>..\packages\PropertyChanged.Fody.3.4.1\lib\net40\PropertyChanged.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Xaml" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="WindowsBase" />
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Page Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="MainWindow.xaml.cs">
+      <DependentUpon>MainWindow.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <None Include="app.config" />
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props'))" />
+    <Error Condition="!Exists('..\packages\Fody.6.6.2\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.6.2\build\Fody.targets'))" />
+    <Error Condition="!Exists('..\packages\PropertyChanged.Fody.3.4.1\build\PropertyChanged.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\PropertyChanged.Fody.3.4.1\build\PropertyChanged.Fody.props'))" />
+  </Target>
+  <Import Project="..\packages\Fody.6.6.2\build\Fody.targets" Condition="Exists('..\packages\Fody.6.6.2\build\Fody.targets')" />
+</Project>

+ 9 - 0
usb_camera_wpf/Air105Camera/App.xaml

@@ -0,0 +1,9 @@
+<Application x:Class="Air105Camera.App"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:local="clr-namespace:Air105Camera"
+             StartupUri="MainWindow.xaml">
+    <Application.Resources>
+         
+    </Application.Resources>
+</Application>

+ 16 - 0
usb_camera_wpf/Air105Camera/App.xaml.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Windows;
+
+namespace Air105Camera
+{
+    /// <summary>
+    /// App.xaml 的交互逻辑
+    /// </summary>
+    public partial class App : Application
+    {
+    }
+}

+ 4 - 0
usb_camera_wpf/Air105Camera/FodyWeavers.xml

@@ -0,0 +1,4 @@
+<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
+  <Costura />
+  <PropertyChanged />
+</Weavers>

+ 160 - 0
usb_camera_wpf/Air105Camera/FodyWeavers.xsd

@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+  <!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
+  <xs:element name="Weavers">
+    <xs:complexType>
+      <xs:all>
+        <xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
+          <xs:complexType>
+            <xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="TriggerDependentProperties" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="EventInvokerNames" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="CheckForEquality" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="SuppressWarnings" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="Costura" minOccurs="0" maxOccurs="1">
+          <xs:complexType>
+            <xs:all>
+              <xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
+                <xs:annotation>
+                  <xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
+                </xs:annotation>
+              </xs:element>
+              <xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
+                <xs:annotation>
+                  <xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
+                </xs:annotation>
+              </xs:element>
+              <xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
+                <xs:annotation>
+                  <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
+                </xs:annotation>
+              </xs:element>
+              <xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
+                <xs:annotation>
+                  <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
+                </xs:annotation>
+              </xs:element>
+              <xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
+                <xs:annotation>
+                  <xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
+                </xs:annotation>
+              </xs:element>
+            </xs:all>
+            <xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="DisableCompression" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="DisableCleanup" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="LoadAtModuleInit" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
+              <xs:annotation>
+                <xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="ExcludeAssemblies" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="IncludeAssemblies" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="Unmanaged32Assemblies" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="Unmanaged64Assemblies" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+            <xs:attribute name="PreloadOrder" type="xs:string">
+              <xs:annotation>
+                <xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
+              </xs:annotation>
+            </xs:attribute>
+          </xs:complexType>
+        </xs:element>
+      </xs:all>
+      <xs:attribute name="VerifyAssembly" type="xs:boolean">
+        <xs:annotation>
+          <xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="VerifyIgnoreCodes" type="xs:string">
+        <xs:annotation>
+          <xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="GenerateXsd" type="xs:boolean">
+        <xs:annotation>
+          <xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
+        </xs:annotation>
+      </xs:attribute>
+    </xs:complexType>
+  </xs:element>
+</xs:schema>

+ 28 - 0
usb_camera_wpf/Air105Camera/MainWindow.xaml

@@ -0,0 +1,28 @@
+<Window x:Class="Air105Camera.MainWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:Air105Camera"
+        mc:Ignorable="d"
+        Title="Air105 Camera Preview" Height="450" Width="500" Loaded="Window_Loaded" Closing="Window_Closing">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        <StackPanel Orientation="Horizontal">
+            <Button x:Name="ConnectButton" Content="Connect" Click="ConnectButton_Click" IsEnabled="{Binding connectEnable}"/>
+            <ComboBox Name="SerialComboBox" MinWidth="100" Margin="5,0"/>
+            <Button x:Name="DisconnectButton" Content="Disconnect" Click="DisconnectButton_Click" IsEnabled="{Binding disconnectEnable}"/>
+            <Button x:Name="RefreshButton" Content="Refresh port list" Click="RefreshButton_Click"/>
+        </StackPanel>
+        <StackPanel Orientation="Horizontal" Grid.Row="1">
+            <TextBlock Text="{Binding totalPack,StringFormat={}packs:{0}}" VerticalAlignment="Center" Margin="10,0,0,0"/>
+            <TextBlock Text="{Binding totalPic,StringFormat={}picture:{0}}" VerticalAlignment="Center" Margin="10,0,0,0"/>
+            <TextBlock Text="{Binding fpsNow,StringFormat={}FPS:{0:F2}}" VerticalAlignment="Center" Margin="10,0,0,0"/>
+        </StackPanel>
+        <Image x:Name="CameraImage"  Canvas.Left="0" Canvas.Top="0" Grid.Row="2"/>
+    </Grid>
+</Window>

+ 319 - 0
usb_camera_wpf/Air105Camera/MainWindow.xaml.cs

@@ -0,0 +1,319 @@
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.IO.Compression;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Brush = System.Drawing.Brush;
+using Color = System.Drawing.Color;
+using Pen = System.Drawing.Pen;
+using PixelFormat = System.Drawing.Imaging.PixelFormat;
+using Rectangle = System.Drawing.Rectangle;
+
+namespace Air105Camera
+{
+    /// <summary>
+    /// MainWindow.xaml 的交互逻辑
+    /// </summary>
+    [PropertyChanged.AddINotifyPropertyChangedInterface]
+    public partial class MainWindow : Window
+    {
+        public MainWindow()
+        {
+            InitializeComponent();
+        }
+
+        //将Bitmap对象转换成bitmapImage对象
+        private BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
+        {
+            MemoryStream stream = new MemoryStream();
+            bitmap.Save(stream, ImageFormat.Bmp);
+            BitmapImage image = new BitmapImage();
+            image.BeginInit();
+            image.StreamSource = stream;
+            image.EndInit();
+            return image;
+        }
+
+        //刷新显示当前图片
+        private void ShowImage()
+        {
+            this.Dispatcher.Invoke(() =>
+            {
+                var temp = ConvertBitmapToBitmapImage(image);
+                CameraImage.Source = temp;
+            });
+        }
+
+        //缓存图片数据
+        Bitmap image = new Bitmap(320, 240, PixelFormat.Format16bppRgb565);
+        Graphics imageGraphic = null;
+        //串口对象
+        private SerialPort Uart = new SerialPort();
+        //总共收到多少包
+        public int totalPack { get; set; } = 0;
+        //总共刷了多少张
+        public int totalPic { get; set; } = 0;
+        //FPS数
+        public double fpsNow { get; set; } = 0;
+        public bool connectEnable { get; set; } = true;
+        public bool disconnectEnable { get; set; } = true;
+
+        private void Window_Loaded(object sender, RoutedEventArgs e)
+        {
+            this.DataContext = this;
+
+            imageGraphic = Graphics.FromImage(image);
+            //刷白
+            imageGraphic.FillRectangle(new SolidBrush(Color.White), 0, 0, image.Width, image.Height);
+
+            //图片显示一下
+            ShowImage();
+
+            // 绑定事件监听,用于监听HID设备插拔
+            (PresentationSource.FromVisual(this) as HwndSource)?.AddHook(WndProc);
+            RefreshPortList();
+
+            //串口数据处理,随便写写
+            new Thread(Uart2Image).Start();
+        }
+
+
+        private void ConnectButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (Uart.IsOpen)
+            {
+                return;
+            }
+            if (((string)SerialComboBox.SelectedItem).Length > 0)
+            {
+                var portName = (string)SerialComboBox.SelectedItem;
+                Task.Run(() =>
+                {
+                    try
+                    {
+                        Uart.PortName = portName;
+                        Uart.Open();
+                    }
+                    catch (Exception ex)
+                    {
+                        MessageBox.Show(ex.Message);
+                    }
+                });
+            }
+        }
+
+        private void DisconnectButton_Click(object sender, RoutedEventArgs e)
+        {
+            if (!Uart.IsOpen)
+            {
+                return;
+            }
+            try
+            {
+                Uart.Close();
+            }
+            catch(Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        private static int UsbPluginDeley = 0;
+        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
+        {
+            if (msg == 0x219)// 监听USB设备插拔消息
+            {
+                if (UsbPluginDeley == 0)
+                {
+                    ++UsbPluginDeley;   // Task启动需要准备时间,这里提前对公共变量加一
+                    Task.Run(() =>
+                    {
+                        do Task.Delay(100).Wait();
+                        while (++UsbPluginDeley < 10);
+                        UsbPluginDeley = 0;
+                        RefreshPortList();
+                    });
+                }
+                else UsbPluginDeley = 1;
+                handled = true;
+            }
+            return IntPtr.Zero;
+        }
+
+        /// <summary>
+        /// 刷新设备列表,并在断开恢复后重连
+        /// </summary>
+        /// <returns>串口列表是否成功获取</returns>
+        private bool RefreshPortList()
+        {
+            bool result = true;
+            var list = SerialPort.GetPortNames();
+            Dispatcher.Invoke(delegate
+            {
+                SerialComboBox.ItemsSource = list;
+                if (SerialComboBox.Items.Count == 0)
+                {
+                    result = false;
+                }
+                else if (SerialComboBox.Items.Contains(Uart.PortName))
+                {
+                    SerialComboBox.SelectedItem = Uart.PortName;
+                }
+                else SerialComboBox.SelectedIndex = 0;
+            });
+            return result;
+        }
+
+        bool closed = false;
+        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+        {
+            closed = true;
+            //直接强关软件
+            Environment.Exit(0);
+        }
+
+        private void RefreshButton_Click(object sender, RoutedEventArgs e)
+        {
+            RefreshPortList();
+        }
+
+        /*
+        16字节头+N字节数据
+        4字节字符串 “VCAM” + 2字节图像宽度 + 2字节图像高度 + 4字节本次数据从哪行开始刷 + 2字节未压缩数据长度 + 2字节压缩后图像数据长度
+        如果压缩前和压缩后数据长度是一致的,那就没有压缩;如果不一致,则后面的N字节要用zlib解压
+        RGB565大端格式
+        */
+        //串口数据处理
+        private void Uart2Image()
+        {
+            byte[] temp = new byte[10 * 1024 * 1024];//缓冲区
+            int p = 0;//上次的位置
+            Stopwatch sw = new Stopwatch();
+            sw.Start();
+            bool getNewData = false;//记一下是不是收到新数据了
+            while (true)
+            {
+                if (closed)
+                    return;
+                connectEnable = !Uart.IsOpen;
+                disconnectEnable = Uart.IsOpen;
+                try
+                {
+                    if (Uart.IsOpen && Uart.BytesToRead > 0)
+                    {
+                        //读数据
+                        var toread = Uart.BytesToRead;
+                        Uart.Read(temp, p, toread);
+                        p += toread;
+                        getNewData = true;
+                    }
+                    else if (!Uart.IsOpen)
+                    {
+                        fpsNow = 0;
+                        p = 0;
+                    }
+                    if(getNewData)
+                    {
+                        getNewData = false;//收到新数据再刷
+                        for (int i = 3; i < p; i++)
+                        {
+                            //匹配VCAM头
+                            if (temp[i - 3] == 'V' && temp[i - 2] == 'C' && temp[i - 1] == 'A' && temp[i] == 'M'
+                                && p - i > 16)
+                            {
+                                var width = BitConverter.ToUInt16(temp, i + 1);
+                                var height = BitConverter.ToUInt16(temp, i + 3);
+                                var start = BitConverter.ToUInt32(temp, i + 5);
+                                var rawLen = BitConverter.ToUInt16(temp, i + 9);
+                                var comLen = BitConverter.ToUInt16(temp, i + 11);
+                                Debug.WriteLine($"found VCAM: {BitConverter.ToString(temp, i - 3, 16)}\r\n" +
+                                    $"{width},{height},{start},{rawLen},{comLen},{i},{p}");
+                                //获取实际数据长度
+                                var realLength = comLen < rawLen ? comLen : rawLen;
+                                if (realLength <= p - i - 13)//一包数据够了
+                                {
+                                    totalPack++;
+                                    Debug.WriteLine($"{realLength} bytes, start unpack");
+
+                                    //存储rgb数据
+                                    byte[] data = null;
+                                    if (comLen < rawLen)//包压缩了
+                                    {
+                                        //Debug.WriteLine(BitConverter.ToString(temp, i + 13, comLen));
+                                        using MemoryStream compressed = new MemoryStream(temp, i + 13, comLen);
+                                        using MemoryStream decompressed = new MemoryStream();
+                                        using InflaterInputStream inputStream = new InflaterInputStream(compressed);
+                                        inputStream.CopyTo(decompressed);
+                                        data = decompressed.ToArray();
+                                    }
+                                    else//没压缩
+                                    {
+                                        Debug.WriteLine($"rawLen,{rawLen},{i + 13 + rawLen}");
+                                        data = new byte[rawLen];
+                                        for (int n = i + 13; n < i + 13 + rawLen; n++)
+                                            data[n - (i + 13)] = temp[n];
+                                    }
+
+                                    //直接把rgb565数据写入bitmap,这样快
+                                    //先把数据改成小端
+                                    for (int n = 0; n < data.Length; n += 2)
+                                    {
+                                        (data[n], data[n + 1]) = (data[n + 1], data[n]);
+                                    }
+                                    var rect = new Rectangle(0, (int)start, width, data.Length / 2 / width);
+                                    BitmapData bmpData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);
+                                    IntPtr ptr = bmpData.Scan0;// Get the address of the first line.
+                                    System.Runtime.InteropServices.Marshal.Copy(data, 0, ptr, data.Length);
+                                    image.UnlockBits(bmpData);
+
+                                    //刷到最后一行时再显示当前图片
+                                    if (start >= height - 16)
+                                    {
+                                        sw.Stop();
+                                        fpsNow = 1000.0 / sw.ElapsedMilliseconds;
+                                        sw.Restart();
+                                        totalPic++;
+                                        ShowImage();
+                                    }
+
+                                    //剩下的数据全部左移到开头
+                                    for (int n = i + 13 + realLength; n < p; n++)
+                                        temp[n - (i + 13 + realLength)] = temp[n];
+                                    p -= i + 13 + realLength;
+                                    Debug.WriteLine($"done, p now {p}");
+                                    if (p > 3)
+                                        i = 2;
+                                    else
+                                        break;
+                                }
+                            }
+                        }
+                    }
+                    Thread.Sleep(1);
+                }
+                catch (Exception e)
+                {
+                    Debug.WriteLine(e.Message);
+                }
+            }
+        }
+    }
+}

+ 55 - 0
usb_camera_wpf/Air105Camera/Properties/AssemblyInfo.cs

@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("Air105Camera")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HP")]
+[assembly: AssemblyProduct("Air105Camera")]
+[assembly: AssemblyCopyright("Copyright © HP 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+//若要开始生成可本地化的应用程序,请设置
+//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
+//例如,如果您在源文件中使用的是美国英语,
+//使用的是美国英语,请将 <UICulture> 设置为 en-US。  然后取消
+//对以下 NeutralResourceLanguage 特性的注释。  更新
+//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+    ResourceDictionaryLocation.None, //主题特定资源词典所处位置
+                                     //(未在页面中找到资源时使用,
+                                     //或应用程序资源字典中找到时使用)
+    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
+                                              //(未在页面中找到资源时使用,
+                                              //、应用程序或任何主题专用资源字典中找到时使用)
+)]
+
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 63 - 0
usb_camera_wpf/Air105Camera/Properties/Resources.Designer.cs

@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Air105Camera.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   一个强类型的资源类,用于查找本地化的字符串等。
+    /// </summary>
+    // 此类是由 StronglyTypedResourceBuilder
+    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
+    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
+    // (以 /str 作为命令选项),或重新生成 VS 项目。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Air105Camera.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 117 - 0
usb_camera_wpf/Air105Camera/Properties/Resources.resx

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 26 - 0
usb_camera_wpf/Air105Camera/Properties/Settings.Designer.cs

@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Air105Camera.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 7 - 0
usb_camera_wpf/Air105Camera/Properties/Settings.settings

@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>

+ 3 - 0
usb_camera_wpf/Air105Camera/app.config

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/></startup></configuration>

+ 7 - 0
usb_camera_wpf/Air105Camera/packages.config

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Costura.Fody" version="4.1.0" targetFramework="net472" />
+  <package id="Fody" version="6.6.2" targetFramework="net472" developmentDependency="true" />
+  <package id="PropertyChanged.Fody" version="3.4.1" targetFramework="net472" />
+  <package id="SharpZipLib" version="1.3.3" targetFramework="net472" />
+</packages>

+ 3 - 3
usb_camera_wpf/README.md

@@ -1,7 +1,7 @@
 # Air105 USB虚拟串口摄像头的上位机程序
 
-C#  + WPF + VS2022 , 水平有限, 就能用的级别
-
-需要配合 core-air105 和 camera_usb demo来使用
+需要使用VS2022
 
+需要配合105最新固件与`LuatOS/demo/camera/Air105/raw_mode`脚本工程来使用
 
+编译好的exe:https://chenxuuu.lanzout.com/if8sM060hfji