Browse Source

单图识别功能完成

djs 5 months ago
parent
commit
a0cf4f6dae

+ 170 - 0
MeterVision/Dlg/EditSingleConfig.xaml

@@ -0,0 +1,170 @@
+<Window x:Class="MeterVision.Dlg.EditSingleConfig"
+        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:MeterVision.Dlg"
+        xmlns:mark="clr-namespace:MeterVision.Mark"
+        mc:Ignorable="d"
+        Background="WhiteSmoke"
+        ResizeMode="CanResizeWithGrip"
+        WindowStartupLocation="CenterOwner"
+        ShowInTaskbar="False"
+        Title="参数设置" Height="600" Width="900">
+    <Grid Margin="10 10 10 0">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="*" />
+            <RowDefinition Height="40" />
+        </Grid.RowDefinitions>
+
+        <Border Grid.Row="0" BorderBrush="#D3D3D3" BorderThickness="1" Background="White">
+            <Grid>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="280" />
+                    <ColumnDefinition Width="10" />
+                    <ColumnDefinition Width="*" />
+                </Grid.ColumnDefinitions>
+
+                <Border Grid.Column="0" BorderBrush="#D3D3D3" BorderThickness="0 0 1 0" Background="White" Padding="0 0 5 0">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="0" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="*" />
+                            <RowDefinition Height="0" />
+                        </Grid.RowDefinitions>
+
+                        <StackPanel Grid.Row="0" Visibility="Collapsed" >
+                            <!--<StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock Text="站点ID:"  FontSize="14px" />
+                            </StackPanel>
+                            <TextBox x:Name="txtStationId" FontSize="14px"  Margin="5" Padding="2"                              
+                             PreviewTextInput="TextBox_PreviewTextInput" />-->
+                        </StackPanel>
+
+                        <StackPanel Grid.Row="1">
+                            <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock Text="表类型:"  FontSize="14px" />
+                                <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                            </StackPanel>
+                            <ComboBox x:Name="cmbMeterType" Margin="5" FontSize="14px"
+                              SelectionChanged="CmbMeterType_SelectionChanged" 
+                              SelectedValuePath="Key" DisplayMemberPath="Value" />
+                        </StackPanel>
+
+                        <Grid Grid.Row="2">
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="*" />
+                                <ColumnDefinition Width="*" />
+                            </Grid.ColumnDefinitions>
+                            <StackPanel Grid.Column="0" >
+                                <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                    <TextBlock Text="每小时流量:"  FontSize="14px" />
+                                    <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                </StackPanel>
+                                <ComboBox x:Name="cmbFlowRate" Margin="5" FontSize="14px"
+                                      IsEditable="True" PreviewTextInput="CmbFlowRate_PreviewTextInput"                              
+                                      SelectionChanged="CmbFlowRate_SelectionChanged"
+                                      SelectedValuePath="Key" DisplayMemberPath="Value" />
+                            </StackPanel>
+                            <StackPanel Grid.Column="1" >
+                                <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                    <TextBlock Text="图片亮度:" FontSize="14px" />
+                                    <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                </StackPanel>
+                                <TextBox x:Name="txtBrightVal" Margin="5" Padding="2" FontSize="14px"/>
+                            </StackPanel>
+                        </Grid>
+
+                        <Grid Grid.Row="3">
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="*" x:Name="grdNumCount" />
+                                <ColumnDefinition Width="*" x:Name="grdIndCount" />
+                            </Grid.ColumnDefinitions>
+                            <StackPanel x:Name="pnlNumCount" Grid.Column="0" >
+                                <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                    <TextBlock Text="数字个数:" FontSize="14px" />
+                                    <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                </StackPanel>
+                                <TextBox x:Name="txtNumCount"  MaxLength="2" Margin="5" Padding="2" FontSize="14px" PreviewTextInput="TextBox_PreviewTextInput" />
+                            </StackPanel>
+
+                            <StackPanel x:Name="pnlIndCount" Grid.Column="1">
+                                <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                    <TextBlock Text="指针个数:" FontSize="14px" />
+                                    <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                </StackPanel>
+                                <TextBox x:Name="txtIndCount"  MaxLength="2" Margin="5" Padding="2" FontSize="14px" PreviewTextInput="TextBox_PreviewTextInput"/>
+                            </StackPanel>
+                        </Grid>
+
+                        <StackPanel x:Name="pnlLastNumUnit" Grid.Row="4">
+                            <StackPanel  Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock x:Name="txtLastUnitTitle" Text="尾数单位:" FontSize="14px" />
+                                <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                <TextBlock Text="" FontSize="13px" />
+                            </StackPanel>
+                            <ComboBox x:Name="cmbLastUnit" Margin="5" FontSize="14px"/>
+                        </StackPanel>
+
+                        <StackPanel Grid.Row="5">
+                            <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock Text="表盘坐标:" FontSize="14px" />
+                                <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                <TextBlock Text=" (左上、右下)" FontSize="13px" />
+                            </StackPanel>
+                            <TextBox x:Name="txtDialRegion" Margin="5" Padding="2" FontSize="14px" />
+                        </StackPanel>
+
+                        <StackPanel x:Name="pnlFeatureRegion" Grid.Row="6">
+                            <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock x:Name="txtFeatureRegionTitle" Text="数字区域\首尾指针坐标:" FontSize="14px" />
+                                <!--<TextBlock Text="*" FontSize="14px" Foreground="Red" />-->
+                                <TextBlock Text="" FontSize="13px" />
+                            </StackPanel>
+                            <TextBox x:Name="txtFeatureRegion" Margin="5" Padding="2" FontSize="14px" />
+                        </StackPanel>
+
+                        <StackPanel Grid.Row="7">
+                            <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock Text="表底读数:" FontSize="14px" />
+                                <TextBlock Text="" FontSize="14px" Foreground="Red" />
+                                <TextBlock Text="" FontSize="13px" />
+                            </StackPanel>
+                            <TextBox x:Name="txtLastValue" Margin="5" Padding="2" FontSize="14px" />
+                        </StackPanel>
+
+                        <StackPanel Grid.Row="8" Visibility="Collapsed" >
+                            <StackPanel Orientation="Horizontal" Margin="5 5 5 0">
+                                <TextBlock Text="读数对应的时间:" FontSize="14px" />
+                                <TextBlock Text="" FontSize="14px" Foreground="Red" />
+                                <TextBlock Text="" FontSize="13px" />
+                            </StackPanel>
+                            <TextBox x:Name="txtLastTime" Margin="5" Padding="2" FontSize="14px" />
+                        </StackPanel>
+
+                    </Grid>
+                </Border>
+
+                <Border Grid.Column="2" Margin="0" BorderBrush="#D3D3D3" BorderThickness="1 0 0 0" Background="White">
+                    <mark:UCMark x:Name="ucMark" />
+                </Border>
+
+            </Grid>
+        </Border>
+
+        <!--按钮区域-->
+        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
+            <Button x:Name="btnOK" Content="确定" Width="80" Height="26" FontSize="14px"
+                    HorizontalAlignment="Center" Margin="0 0 20 0" Click="BtnOK_Click"/>
+            <Button x:Name="btnClose" Content="取消" Width="80" Height="26" FontSize="14px"
+                    VerticalAlignment="Center" HorizontalAlignment="Center" Click="BtnClose_Click"/>
+        </StackPanel>
+
+    </Grid>
+</Window>

+ 322 - 0
MeterVision/Dlg/EditSingleConfig.xaml.cs

@@ -0,0 +1,322 @@
+using MeterVision.db;
+using MeterVision.model;
+using MeterVision.Util;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+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.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace MeterVision.Dlg
+{
+    /// <summary>
+    /// EditSingleConfig.xaml 的交互逻辑
+    /// </summary>
+    public partial class EditSingleConfig : Window
+    {
+        public SingleDetailItem mDetailItem { get; private set; }
+        private bool _can_mark;
+
+
+        public EditSingleConfig(SingleDetailItem detailItem)
+        {
+            InitializeComponent();
+            cmbMeterType.ItemsSource = StationItem.MeterTypeList;
+            cmbFlowRate.ItemsSource = StationItem.FlowRateList;
+            cmbLastUnit.ItemsSource = StationItem.UnitList;
+
+            mDetailItem = detailItem;
+            Init_Load(detailItem);
+
+            if (File.Exists(detailItem.SrcImage))
+            {
+                ucMark.Visibility = Visibility.Visible;
+                ucMark.SetImagePath(detailItem.SrcImage);
+                ucMark.MeterRegion_MarkFinished += UcMark_MeterRegion_MarkFinished;
+                ucMark.FeatureRegion_MarkFinished += UcMark_FeatureRegion_MarkFinished;
+                _can_mark = true;
+            }
+            else
+            {
+                ucMark.Visibility = Visibility.Hidden;
+                _can_mark = false;
+            }
+
+            if (_can_mark)
+            {
+                ucMark.MeterType = detailItem.MeterType;
+            }
+
+        }
+
+        private void UcMark_FeatureRegion_MarkFinished(string regions, int meterType)
+        {
+            //throw new NotImplementedException();
+            this.txtFeatureRegion.Text = regions;
+        }
+
+        private void UcMark_MeterRegion_MarkFinished(string regions, int meterType)
+        {
+            //throw new NotImplementedException();
+            this.txtDialRegion.Text = regions;
+        }
+
+        private void Init_Load(SingleDetailItem detailItem)
+        {
+            this.ChangeInfoByMeterType(detailItem.MeterType);
+
+            cmbMeterType.SelectedValue = detailItem.MeterType;
+            cmbFlowRate.Text = detailItem.FlowRate.ToString();
+            txtBrightVal.Text = detailItem.BrightVal.ToString();
+            txtNumCount.Text = detailItem.DigitCount == 0 ? "" : detailItem.DigitCount.ToString();
+            txtIndCount.Text = detailItem.PointerCount == 0 ? "" : detailItem.PointerCount.ToString();
+
+            txtDialRegion.Text = detailItem.MeterRegion;
+            if (detailItem.MeterType == 1 || detailItem.MeterType == 3 || detailItem.MeterType == 2)
+            {
+                cmbLastUnit.SelectedValue = double.Parse(detailItem.LastUnit);
+                txtFeatureRegion.Text = detailItem.FeatureRegion;
+            }
+
+            txtLastValue.Text = detailItem.LastValue.ToString();
+                //string.IsNullOrEmpty(detailItem.LastTime) ? "" : detailItem.LastValue.ToString();
+            txtLastTime.Text = detailItem.LastTime;
+        }
+
+        private bool Get_Load()
+        {
+            bool trySuccess = false;
+
+            int meterType = -1;
+            if (cmbMeterType.SelectedItem != null)
+            {
+                meterType = (int)cmbMeterType.SelectedValue;
+            }
+            if (meterType == 0)
+            {
+                MessageBox.Show("请选择正确的表类型");
+                return false;
+            }
+
+
+            int flowRate = -1;
+            if (cmbFlowRate.SelectedItem != null)
+            {
+                flowRate = (int)cmbFlowRate.SelectedValue;
+            }
+            else
+            {
+                trySuccess = int.TryParse(cmbFlowRate.Text.Trim(), out flowRate);
+            }
+            if (flowRate == 0)
+            {
+                MessageBox.Show("请输入每小时最大流量");
+                return false;
+            }
+
+            double brightVal = -1;
+            trySuccess = double.TryParse(txtBrightVal.Text.Trim(), out brightVal);
+            if (brightVal == 0)
+            {
+                brightVal = 1.2;
+            }
+
+
+            int numCount = -1;
+            trySuccess = int.TryParse(txtNumCount.Text.Trim(), out numCount);
+            if ((meterType == 1 || meterType == 3) && numCount < 4)
+            {
+                MessageBox.Show("请输入正确的数字个数");
+                return false;
+            }
+
+            int indCount = -1;
+            trySuccess = int.TryParse(txtIndCount.Text.Trim(), out indCount);
+            if (meterType == 2 && indCount < 5)
+            {
+                MessageBox.Show("请输入正确的指针个数");
+                return false;
+            }
+
+            double lastUnit = 0;
+            if (cmbLastUnit.SelectedValue == null)
+            {
+                MessageBox.Show("请选择尾数单位");
+                return false;
+            }
+            lastUnit = (double)cmbLastUnit.SelectedValue;
+
+
+
+            string dialRegion = txtDialRegion.Text.Trim();
+            if (string.IsNullOrEmpty(dialRegion))
+            {
+                MessageBox.Show("请输入表盘坐标");
+                return false;
+            }
+
+            string featureRegion = txtFeatureRegion.Text.Trim();
+            if ((meterType == 1 || meterType == 3) && string.IsNullOrEmpty(featureRegion))
+            {
+                MessageBox.Show("请输入数字区域 \\ 首位指针坐标");
+                return false;
+            }
+
+            double lastValue;
+            trySuccess = double.TryParse(txtLastValue.Text.Trim(), out lastValue);
+
+            string lastTime = txtLastTime.Text.Trim();
+
+            mDetailItem .MeterType = meterType;
+            mDetailItem.FlowRate = flowRate;
+            mDetailItem.BrightVal = brightVal;
+            mDetailItem.DigitCount = numCount;
+            mDetailItem.PointerCount = indCount;
+            mDetailItem.LastUnit = "";
+            mDetailItem.FeatureRegion = "";
+            if (meterType == 1 || meterType == 3 || meterType == 2)
+            {
+                mDetailItem.LastUnit = lastUnit.ToString();
+                mDetailItem.FeatureRegion = featureRegion;
+            }
+
+            mDetailItem.MeterRegion = dialRegion;
+            mDetailItem.LastValue = lastValue;
+
+            if (string.IsNullOrEmpty(lastTime))
+            {
+                lastTime = DateTime.Now.ToString("yyyyMMddHHmmss");
+            }
+
+            mDetailItem.LastTime = lastTime;
+
+            return true;
+        }
+
+        private void BtnOK_Click(object sender, RoutedEventArgs e)
+        {
+            bool blGet = Get_Load();
+            if (!blGet)
+            {
+                return;
+            }
+
+            TSingleDetail singleDetail = new TSingleDetail();
+            ObjectHelper.CopyMatchingFields(mDetailItem,singleDetail);
+
+            try
+            {
+                bool updateSuccess = DBSingle.UpdateSingleDetailByConfig(singleDetail);
+                if (updateSuccess)
+                {
+                    DialogResult = true;
+                }
+                else
+                {
+                    MessageBox.Show(Application.Current.MainWindow, "修改失败", "错误",
+                        MessageBoxButton.OK, MessageBoxImage.Error);
+                }
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(Application.Current.MainWindow, $"{ex.Message}", "错误",
+                    MessageBoxButton.OK, MessageBoxImage.Error);
+            }
+
+        }
+
+        private void BtnClose_Click(object sender, RoutedEventArgs e)
+        {
+            DialogResult = false;
+            this.Close();
+        }
+
+        private void CmbMeterType_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            int meterType = -1; //= int.Parse(cmbMeterType.SelectedValue.ToString());
+                                //((KeyValuePair<int, string>)cmbMeterType.SelectedItem).Key;
+                                //int.TryParse(cmbMeterType.SelectedValue.ToString(),out meterType);
+            if (cmbMeterType.SelectedValue != null)
+            {
+                meterType = (int)cmbMeterType.SelectedValue;
+            }
+            this.ChangeInfoByMeterType(meterType);
+
+            //改变仪表的类型
+            ucMark.MeterType = meterType;
+        }
+
+        private void ChangeInfoByMeterType(int meterType)
+        {
+            if (meterType == 1 || meterType == 3)
+            {
+                txtLastUnitTitle.Text = "最后一位数字的单位:";
+                txtFeatureRegionTitle.Text = "数字区域坐标:";
+                if (meterType == 1)
+                {
+                    grdIndCount.Width = new GridLength(1, GridUnitType.Star);
+                    grdNumCount.Width = new GridLength(1, GridUnitType.Star);
+                }
+                else
+                {
+                    grdIndCount.Width = new GridLength(0, GridUnitType.Pixel);
+                    grdNumCount.Width = new GridLength(1, GridUnitType.Star);
+                }
+            }
+            else if (meterType == 2)
+            {
+                txtLastUnitTitle.Text = "最后一个指针的单位:";
+                txtFeatureRegionTitle.Text = "首位指针同刻度坐标:";
+                grdNumCount.Width = new GridLength(0, GridUnitType.Pixel);
+                grdIndCount.Width = new GridLength(1, GridUnitType.Star);
+            }
+        }
+
+        private void CmbFlowRate_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+
+        }
+
+        private void CmbFlowRate_PreviewTextInput(object sender, TextCompositionEventArgs e)
+        {
+            // 只允许数字输入
+            e.Handled = !Regex.IsMatch(e.Text, @"^\d$");
+        }
+
+
+        private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
+        {
+            // 如果输入法启用(例如中文输入法),则阻止输入
+            //if (InputMethod.GetIsInputMethodEnabled)
+            //{
+            //    e.Handled = true;
+            //    return;
+            //}
+
+            // 只允许数字输入
+            //e.Handled = !Regex.IsMatch(e.Text, @"^\d$");
+            // 判断当前输入的字符是否是数字
+            if (e.Text.All(char.IsDigit))
+            {
+                e.Handled = false;  // 如果是数字,允许输入
+            }
+            else
+            {
+                // 如果当前输入的字符不是数字,并且是中文输入法的拼音或候选字,禁止输入
+                e.Handled = true;
+            }
+        }
+        //-----------------------------------------------------
+        //---------------------------------------------------
+    }
+}

+ 0 - 75
MeterVision/Dlg/EditStationDlg.xaml

@@ -1,75 +0,0 @@
-<Window x:Class="MeterVision.Dlg.EditStationDlg"
-        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:MeterVision.Dlg"
-        mc:Ignorable="d"
-        Title="站点管理" Height="520" Width="400">
-    <Grid Margin="0">
-        <Grid.RowDefinitions>
-            <RowDefinition Height="*" />
-            <RowDefinition Height="40" />
-        </Grid.RowDefinitions>
-
-        <Border Grid.Row="0" BorderBrush="#D3D3D3" BorderThickness="1" Margin="10 10 10 0" Background="White">
-            <Grid Margin="10">
-                <Grid.ColumnDefinitions>
-                    <ColumnDefinition Width="60" />
-                    <ColumnDefinition Width="*" />
-                    <ColumnDefinition Width="20" />
-                    <ColumnDefinition Width="60" />
-                    <ColumnDefinition Width="*" />
-                </Grid.ColumnDefinitions>
-                <Grid.RowDefinitions>
-                    <RowDefinition Height="40" />
-                    <RowDefinition Height="40" />
-                    <RowDefinition Height="40" />
-                    <RowDefinition Height="40" />
-                    <RowDefinition Height="40" />
-                    <RowDefinition Height="*" />
-                    <RowDefinition Height="*" />
-                    <RowDefinition Height="*" />
-                </Grid.RowDefinitions>
-
-                <TextBlock Grid.Row="0" Grid.Column="0" Text="站点ID" FontSize="14" VerticalAlignment="Center" />
-                <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtStationId" FontSize="14" Height="28" />
-
-                <TextBlock Grid.Row="0" Grid.Column="3" Text="站名" FontSize="14" VerticalAlignment="Center" />
-                <TextBox Grid.Row="0" Grid.Column="4" x:Name="txtStationName" FontSize="14" Height="28" />
-
-                <TextBlock Grid.Row="1" Grid.Column="0" Text="表类型" FontSize="14" VerticalAlignment="Center" />
-                <ComboBox Grid.Row="1" Grid.Column="1" x:Name="cmbMeterType" Height="28" >
-                    <ComboBoxItem Content="0" />
-                    <ComboBoxItem Content="1" />
-                    <ComboBoxItem Content="2" />
-                    <ComboBoxItem Content="3" />
-                </ComboBox>
-
-                <TextBlock Grid.Row="1" Grid.Column="3" Text="每小时最大流量" TextWrapping="Wrap"
-                           FontSize="14" VerticalAlignment="Center" />
-
-                <ComboBox x:Name="cmbFlowRate" Grid.Row="1" Grid.Column="4" Height="28">
-                    <ComboBoxItem Content="0" />
-                    <ComboBoxItem Content="50" />
-                    <ComboBoxItem Content="100" />
-                    <ComboBoxItem Content="150" />
-                    <ComboBoxItem Content="200" />
-                </ComboBox>
-                
-                
-
-            </Grid>             
-        </Border>
-        
-        <!--按钮区域-->
-        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
-            <Button x:Name="btnOK" Content="确定" Width="80" Height="26"
-                    HorizontalAlignment="Center" Margin="0 0 20 0" Click="BtnOK_Click"/>
-            <Button x:Name="btnClose" Content="取消" Width="80" Height="26"
-                    VerticalAlignment="Center" HorizontalAlignment="Center" Click="BtnClose_Click"/>
-        </StackPanel>
-        
-    </Grid>
-    
-</Window>

+ 0 - 42
MeterVision/Dlg/EditStationDlg.xaml.cs

@@ -1,42 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-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.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
-
-namespace MeterVision.Dlg
-{
-    /// <summary>
-    /// EditStation.xaml 的交互逻辑
-    /// </summary>
-    public partial class EditStationDlg : Window
-    {
-        public EditStationDlg()
-        {
-            InitializeComponent();
-        }
-
-        private void SaveButton_Click(object sender, RoutedEventArgs e)
-        {
-
-        }
-
-        private void BtnOK_Click(object sender, RoutedEventArgs e)
-        {
-
-        }
-
-        private void BtnClose_Click(object sender, RoutedEventArgs e)
-        {
-
-        }
-    }
-}

+ 169 - 29
MeterVision/FreeAi/FaBefore.cs

@@ -1,7 +1,9 @@
-using System;
+using MeterVision.model;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using static MeterVision.FreeAi.FaImport;
 
@@ -12,44 +14,182 @@ namespace MeterVision.FreeAi
     {
 
         //给beforeAI设置默认值
-        private static void setDefaultValue_BeforeAI(ref BeforeAI beforeAI)
+        //private static void setDefaultValue_BeforeAI(ref BeforeAI beforeAI)
+        //{
+        //    beforeAI.AI_result = FaConstant.IMPOSSIBLE_MAX_VALUE;
+        //    beforeAI.result_of_1st_part = FaConstant.IMPOSSIBLE_MAX_VALUE;
+        //    beforeAI.result_of_2st_part = FaConstant.IMPOSSIBLE_MAX_VALUE;
+        //    beforeAI.big_jpg_max_size = 12 * 1024;
+        //    beforeAI.bright_factor = 1.2f;
+        //    beforeAI.center_x = FaConstant.IMPOSSIBLE_VALUE;
+        //    beforeAI.center_y = FaConstant.IMPOSSIBLE_VALUE;
+        //    beforeAI.lean_angle = FaConstant.IMPOSSIBLE_VALUE;
+        //    beforeAI.is_nums_in_upper = 1;
+        //    beforeAI.is_OK_preValue = 0;
+        //    beforeAI.is_upload_red_ind_part = 1;
+        //    beforeAI.manual_parameter_enable = 0;
+        //    beforeAI.MAX_FLOW_PER_HOUR = FaConstant.IMPOSSIBLE_MAX_VALUE;
+        //    beforeAI.meter_type = 0;
+        //    beforeAI.NUM_BLACK_NUMBERS = FaConstant.IMPOSSIBLE_NUM_NUMBER;
+        //    beforeAI.NUM_NUMBERS = FaConstant.IMPOSSIBLE_NUM_NUMBER;
+        //    beforeAI.NUM_INDICATORS = FaConstant.IMPOSSIBLE_NUM_NUMBER;
+        //    beforeAI.UNIT_OF_THE_LAST_NUMBER = FaConstant.ONE_STERE;
+        //    beforeAI.compress_jpg_index = 0;
+        //    beforeAI.number_image_zoomrate = 1.0f;
+        //    beforeAI.indicator_image_zommrate = 1.0f;
+        //    beforeAI.use_test_jpg_flag = 1;
+        //    beforeAI.is_get_whole_image = 1;
+
+        //    beforeAI.num_region = new ushort[4];
+        //    beforeAI.config_num_region = new ushort[4];
+        //    beforeAI.indicators_regions = new ushort[10 * 4];
+        //}
+
+        public static BeforeAI getBeforeAI()
+        {
+            BeforeAI beforeAI = new BeforeAI();
+            //setDefaultValue_BeforeAI(ref beforeAI);
+
+            return beforeAI;
+        }
+
+        private static void SetDefaultValue(ref BeforeAI beforeAI)
         {
-            beforeAI.AI_result = FaConstant.IMPOSSIBLE_MAX_VALUE;
-            beforeAI.result_of_1st_part = FaConstant.IMPOSSIBLE_MAX_VALUE;
-            beforeAI.result_of_2st_part = FaConstant.IMPOSSIBLE_MAX_VALUE;
-            beforeAI.big_jpg_max_size = 12 * 1024;
-            beforeAI.bright_factor = 1.2f;
-            beforeAI.center_x = FaConstant.IMPOSSIBLE_VALUE;
-            beforeAI.center_y = FaConstant.IMPOSSIBLE_VALUE;
-            beforeAI.lean_angle = FaConstant.IMPOSSIBLE_VALUE;
-            beforeAI.is_nums_in_upper = 1;
-            beforeAI.is_OK_preValue = 0;
-            beforeAI.is_upload_red_ind_part = 1;
-            beforeAI.manual_parameter_enable = 0;
-            beforeAI.MAX_FLOW_PER_HOUR = FaConstant.IMPOSSIBLE_MAX_VALUE;
-            beforeAI.meter_type = 0;
-            beforeAI.NUM_BLACK_NUMBERS = FaConstant.IMPOSSIBLE_NUM_NUMBER;
+            beforeAI.METER_TYPE = FaConstant.IMPOSSIBLE_NUM_NUMBER;
             beforeAI.NUM_NUMBERS = FaConstant.IMPOSSIBLE_NUM_NUMBER;
+            beforeAI.UNIT_OF_THE_LAST_NUMBER = (uint)FaConstant.IMPOSSIBLE_VALUE;
             beforeAI.NUM_INDICATORS = FaConstant.IMPOSSIBLE_NUM_NUMBER;
-            beforeAI.UNIT_OF_THE_LAST_NUMBER = FaConstant.ONE_STERE;
-            beforeAI.compress_jpg_index = 0;
-            beforeAI.number_image_zoomrate = 1.0f;
-            beforeAI.indicator_image_zommrate = 1.0f;
-            beforeAI.use_test_jpg_flag = 1;
-            beforeAI.is_get_whole_image = 1;
-
-            beforeAI.num_region = new ushort[4];
-            beforeAI.config_num_region = new ushort[4];
-            beforeAI.indicators_regions = new ushort[10 * 4];
+            beforeAI.UNIT_OF_THE_LAST_INDICATOR = (uint)FaConstant.IMPOSSIBLE_VALUE;
+            beforeAI.MAX_FLOW_THRESHOLD = FaConstant.IMPOSSIBLE_MAX_VALUE;
+            beforeAI.AI_RESULT = FaConstant.IMPOSSIBLE_MAX_VALUE;
+            beforeAI.AI_COMPLETE_RESULT = FaConstant.IMPOSSIBLE_MAX_VALUE;
+            beforeAI.CONFIG_NUM_REGION = new ushort[8];
+            for(int i = 0; i < 8; i++)
+            {
+                beforeAI.CONFIG_NUM_REGION[i] = (ushort)FaConstant.IMPOSSIBLE_VALUE;
+            }
+            beforeAI.CONFIG_TWO_IND = new ushort[4];
+            for(int i = 0; i < 4; i++)
+            {
+                beforeAI.CONFIG_TWO_IND[i] = (ushort)FaConstant.IMPOSSIBLE_VALUE;
+            }
+            beforeAI.CONFIG_METER_REGION = new ushort[4];
+            for(int i = 0; i < 4; i++)
+            {
+                beforeAI.CONFIG_METER_REGION[i] = (ushort)FaConstant.IMPOSSIBLE_VALUE;
+            }
+            beforeAI.BRIGHT_FACTOR = 1.20f;
+            beforeAI.BIG_JPG_SIZE = 12 * 1024;
+            beforeAI.USE_TEST_JPG_FLAG = 1;
+            beforeAI.COMPRESS_JPG_INDEX = 100;
+            beforeAI.IS_UPLOAD_RED_IND_PART = 1;
         }
 
-        public static BeforeAI getBeforeAI()
+        public static BeforeAI getBeforeAI(SingleDetailItem detailItem)
         {
             BeforeAI beforeAI = new BeforeAI();
-            setDefaultValue_BeforeAI(ref beforeAI);
+            SetDefaultValue(ref beforeAI);
+
+            beforeAI.METER_TYPE = (byte)detailItem.MeterType;
+           
+
+            if (beforeAI.METER_TYPE == 1 || beforeAI.METER_TYPE == 3)
+            {
+                beforeAI.NUM_NUMBERS = (byte)detailItem.DigitCount;
+                beforeAI.NUM_INDICATORS = (byte)detailItem.PointerCount;
+                beforeAI.UNIT_OF_THE_LAST_NUMBER = (uint)(double.Parse(detailItem.LastUnit) * FaConstant.CUBE_VALUE);
+                
+                TryParseCoordinates(detailItem.FeatureRegion, 4, ref beforeAI.CONFIG_NUM_REGION);
+            }
+            else if(beforeAI.METER_TYPE == 2)
+            {
+                beforeAI.NUM_INDICATORS = (byte)detailItem.PointerCount;
+                beforeAI.UNIT_OF_THE_LAST_INDICATOR = (uint)(double.Parse(detailItem.LastUnit) * FaConstant.CUBE_VALUE);
+                TryParseCoordinates(detailItem.FeatureRegion, 2, ref beforeAI.CONFIG_TWO_IND);
+            }
+
+            if (beforeAI.METER_TYPE == 1 || beforeAI.METER_TYPE == 2 || beforeAI.METER_TYPE == 3)
+            {
+                TryParseCoordinates(detailItem.MeterRegion, 2, ref beforeAI.CONFIG_METER_REGION);
+
+                //单张图计算最大流量,意义不大,也不太好确定时间,就按1小时计算吧
+                beforeAI.MAX_FLOW_THRESHOLD = (ulong)detailItem.FlowRate * FaConstant.CUBE_VALUE;
+                beforeAI.AI_RESULT = (ulong)detailItem.LastValue * FaConstant.CUBE_VALUE;
+            }
+            beforeAI.BRIGHT_FACTOR = (float)detailItem.BrightVal;
+            beforeAI.USE_TEST_JPG_FLAG = 1; //(float)detailItem.use
+            beforeAI.COMPRESS_JPG_INDEX = 100;
+            beforeAI.IS_UPLOAD_RED_IND_PART = 1;    //这个应该价格配置项
 
             return beforeAI;
         }
 
+
+        static bool TryParseCoordinates(string input, int numPairs, ref ushort[] numbers)
+        {
+            // 确保传入的数组足够大
+            if (numbers.Length < numPairs * 2)
+            {
+                Console.WriteLine("错误: 数组长度不足");
+                return false;
+            }
+
+            // 生成正则表达式,例如匹配 4 组时:"(\d+),(\d+) (\d+),(\d+) (\d+),(\d+) (\d+),(\d+)"
+            string pattern = "^" + string.Join(" ", Enumerable.Repeat(@"(\d+),(\d+)", numPairs)) + "$";
+            Match match = Regex.Match(input, pattern);
+
+            if (match.Success)
+            {
+                for (int i = 0; i < numPairs * 2; i++)
+                {
+                    numbers[i] = ushort.Parse(match.Groups[i + 1].Value);
+                }
+                return true;
+            }
+
+            return false;
+        }
+
+        //static bool TryParseCoordinates(string input, int numPairs, out int[] numbers)
+        //{
+        //    numbers = new int[numPairs * 2];
+
+        //    // 生成正则表达式,例如匹配 4 组时:"(\d+),(\d+) (\d+),(\d+) (\d+),(\d+) (\d+),(\d+)"
+        //    string pattern = "^" + string.Join(" ", new string[numPairs].Select(_ => @"(\d+),(\d+)")) + "$";
+        //    Match match = Regex.Match(input, pattern);
+
+        //    if (match.Success)
+        //    {
+        //        for (int i = 0; i < numPairs * 2; i++)
+        //        {
+        //            numbers[i] = int.Parse(match.Groups[i + 1].Value);
+        //        }
+        //        return true;
+        //    }
+
+        //    return false;
+        //}
+
+        //static bool TryParseCoordinates4(string input, out int[] numbers)
+        //{
+        //    numbers = new int[8];
+
+        //    // 正则表达式匹配 4 组 "数字,数字",中间用空格分隔
+        //    string pattern = @"^(\d+),(\d+) (\d+),(\d+) (\d+),(\d+) (\d+),(\d+)$";
+        //    Match match = Regex.Match(input, pattern);
+
+        //    if (match.Success)
+        //    {
+        //        // 提取匹配到的 8 个数值
+        //        for (int i = 0; i < 8; i++)
+        //        {
+        //            numbers[i] = int.Parse(match.Groups[i + 1].Value);
+        //        }
+        //        return true;
+        //    }
+
+        //    return false;
+        //}
+        //--------------------------------------------------------
     }
 }

+ 2 - 0
MeterVision/FreeAi/FaConstant.cs

@@ -28,5 +28,7 @@ namespace MeterVision.FreeAi
 
         public static ushort IMAGE_WIDTH = 320;
 
+        public static uint CUBE_VALUE = 10000;
+
     }
 }

+ 48 - 302
MeterVision/FreeAi/FaImport.cs

@@ -9,332 +9,78 @@ namespace MeterVision.FreeAi
 {
     public class FaImport
     {
-        //beforeAI结构体
-        //[StructLayout(LayoutKind.Sequential, Pack = 1)]
-        //public struct BeforeAI
-        //{
-        //    // 01 (配置信息)数字个数(黑色+红色)
-        //    public byte NUM_NUMBERS;          // uint8_t 对应 byte
-
-        //    // 01 (配置信息)数字个数(黑色),人工决定的时候不需要配置,=NUM_NUMBERS
-        //    public byte NUM_BLACK_NUMBERS;    // uint8_t 对应 byte
-
-        //    // 02(配置信息)指针个数
-        //    public byte NUM_INDICATORS;       // uint8_t 对应 byte
-
-        //    // 1立方=1000L = 10000分升,1分升=1/10升=1/10000立方
-        //    // 尾数单位的含义,最小单位1分升,1dL, 0.1L(万分之一立方)
-        //    public uint UNIT_OF_THE_LAST_NUMBER; // uint32_t 对应 uint
-
-        //    // 04(配置信息)强制拉回的概率阈值(初始=50%)
-        //    public byte BEST_CONF;            // uint8_t 对应 byte
-
-        //    // 05(配置信息)识别可信度的概率阈值(初始=5%)
-        //    public byte GOOD_CONF;            // uint8_t 对应 byte
-
-        //    // 06(配置信息)单独每一位概率阈值(初始=90%)
-        //    public byte BIT_BEST_CONF;        // uint8_t 对应 byte
-
-        //    // 07(配置信息)是否颠倒安装(0=正常安装;1=上下颠倒安装)
-        //    public byte IS_UPDOWN;            // uint8_t 对应 byte
-
-        //    // 每小时的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
-        //    public ulong MAX_FLOW_PER_HOUR;   // uint64_t 对应 ulong
-
-        //    // 当前时间距离上次准确识别时间内的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
-        //    public ulong MAX_FLOW_threshold;  // uint64_t 对应 ulong
-
-        //    // 09(本次结果)图像倾斜角度
-        //    public short lean_angle;          // int16_t 对应 short
-
-        //    // 8字节整数,表示识别结果,最小单位分升,即1dL=0.1L=1m³/10000),万分之一立方;
-        //    public ulong AI_result;           // uint64_t 对应 ulong
-
-        //    // 传入的ai_result的值是否是可信的前值(来源于AfterAI);
-        //    public byte is_OK_preValue;       // uint8_t 对应 byte
-
-        //    // 11(上次结果)数字区域的xmin、ymin、xmax、ymax坐标值
-        //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-        //    public ushort[] num_region;       // uint16_t 对应 ushort, 4 个元素的数组
-
-        //    // 12 是否需要上传整图?0=不需要;1=需要;
-        //    public byte is_get_whole_image;   // uint8_t 对应 byte
-
-        //    // 上传日志的printf回调函数
-        //    public IntPtr printfAiLog;        // 回调函数指针, 用 IntPtr 表示
-
-        //    // 14(配置信息)公司平台上传的图片的存储大小?(1:BMP彩色图; >1:JPG彩色图)
-        //    public ushort PingTai_image_size; // uint16_t 对应 ushort
-
-        //    // 15(配置信息)上传图像尺寸大小 = 数字区域区域外扩 PingTai_image_margin 像素
-        //    public byte PingTai_image_margin; // uint8_t 对应 byte
-
-        //    // 16(配置信息)数字区域的xmin、ymin、xmax、ymax坐标值
-        //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-        //    public ushort[] config_num_region; // uint16_t 对应 ushort, 4 个元素的数组
-
-        //    // 17(配置信息)图像倾斜角度
-        //    public short config_lean_angle;   // int16_t 对应 short
-
-        //    // 18(配置信息)在识别过程中,是否使用 config_num_region、config_lean_angle变量值
-        //    public byte is_use_config_info;   // uint8_t 对应 byte
-
-        //    // 19(配置信息)是否上传CNN模型的检测结果到服务器?(0=不上传;1=上传)
-        //    public byte is_upload_cnn_output; // uint8_t 对应 byte
-
-        //    // 20(配置信息)对CNN输出进行解码时,使用的NMS阈值(默认值=10)
-        //    public byte nms_threshold;        // uint8_t 对应 byte
-
-        //    // 21(配置信息)是否使用检测到的大框来排除数字(0=不用;1=用)(默认值=1)
-        //    public byte is_use_detected_num88;// uint8_t 对应 byte
-
-        //    // 22(上次结果),存储每个指针区域的xmin、ymin、xmax、ymax坐标值。最多存储10个指针
-        //    //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
-        //    //public ushort[,] indicators_regions; // uint16_t[10][4] 对应 ushort[10, 4]
-        //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
-        //    public ushort[] indicators_regions; // 对应 uint16_t[10][4] 展开成一维数组
-
-        //    // 表具中心点x
-        //    public float center_x;            // float 对应 float
-
-        //    // 表具中心点y
-        //    public float center_y;            // float 对应 float
-
-        //    // 图片亮度
-        //    public float bright_factor;       // float 对应 float
-
-        //    // 仪表类型,模型类型
-        //    public byte meter_type;           // uint8_t 对应 byte
-
-        //    // 人工配置的水表参数使能开关
-        //    public byte manual_parameter_enable; // uint8_t 对应 byte
-
-        //    // 大图JPG图像的最大字节数
-        //    public ushort big_jpg_max_size;   // uint16_t 对应 ushort
-
-        //    // AI输出JPG图是,使用测试图标记,1:使用,0:不使用
-        //    public byte use_test_jpg_flag;    // uint8_t 对应 byte
-
-        //    // 数字水表图像放大倍率,中心点为图像中心
-        //    public float number_image_zoomrate;   // float 对应 float
-
-        //    // 指针水表图像放大倍率,中心点为拟合圆圆心
-        //    public float indicator_image_zommrate;// float 对应 float
-
-        //    // 水表中数字区域位于上半部?1=位于上半部分;0=位于下半部分。 默认值为1
-        //    public byte is_nums_in_upper;     // uint8_t 对应 byte
-
-        //    // 数字+指针水表中“数字”部分读数;全指针水表中“黑色”指针部分的读数;全数字水表中“数字”部分读数
-        //    public ulong result_of_1st_part;  // uint64_t 对应 ulong
-
-        //    // 数字+指针水表中“指针”部分读数;全指针水表中“红色”指针部分的读数;用来判断此水表是否在动
-        //    public ulong result_of_2st_part;  // uint64_t 对应 ulong
-
-        //    // 压缩JPG的索引,来自after_struct,首次索引为0
-        //    public byte compress_jpg_index;   // uint8_t 对应 byte
-
-        //    // 是否上传红色指针部分的数据(0:不上传;1:上传;默认1)
-        //    public byte is_upload_red_ind_part;// uint8_t 对应 byte
-        //}
-
-
         [StructLayout(LayoutKind.Sequential, Pack = 1)]
         public struct BeforeAI
         {
-            // 01 (配置信息)数字个数(黑色+红色)
+            //01 仪表类型
+            public byte METER_TYPE;          // uint8_t 对应 byte
+            //02 数字个数(黑色+红色)
             public byte NUM_NUMBERS;          // uint8_t 对应 byte
-
-            // 01 (配置信息)数字个数(黑色),人工决定的时候不需要配置,=NUM_NUMBERS
-            public byte NUM_BLACK_NUMBERS;    // uint8_t 对应 byte
-
-            // 02(配置信息)指针个数
-            public byte NUM_INDICATORS;       // uint8_t 对应 byte
-
-            // 1立方=1000L = 10000分升,1分升=1/10升=1/10000立方
-            // 尾数单位的含义,最小单位1分升,1dL, 0.1L(万分之一立方)
+            //03 尾数单位的含义,最小单位1分升,1dL, 0.1L(万分之一立方)
             public uint UNIT_OF_THE_LAST_NUMBER; // uint32_t 对应 uint
-
-            // 04(配置信息)强制拉回的概率阈值(初始=50%)
-            public byte BEST_CONF;            // uint8_t 对应 byte
-
-            // 05(配置信息)识别可信度的概率阈值(初始=5%)
-            public byte GOOD_CONF;            // uint8_t 对应 byte
-
-            // 06(配置信息)单独每一位概率阈值(初始=90%)
-            public byte BIT_BEST_CONF;        // uint8_t 对应 byte
-
-            // 07(配置信息)是否颠倒安装(0=正常安装;1=上下颠倒安装)
-            public byte IS_UPDOWN;            // uint8_t 对应 byte
-
-            // 每小时的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
-            public ulong MAX_FLOW_PER_HOUR;   // uint64_t 对应 ulong
-
-            // 当前时间距离上次准确识别时间内的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
-            public ulong MAX_FLOW_threshold;  // uint64_t 对应 ulong
-
-            // 09(本次结果)图像倾斜角度
-            public short lean_angle;          // int16_t 对应 short
-
-            // 8字节整数,表示识别结果,最小单位分升,即1dL=0.1L=1m³/10000),万分之一立方;
-            public ulong AI_result;           // uint64_t 对应 ulong
-
-            // 传入的ai_result的值是否是可信的前值(来源于AfterAI);
-            public byte is_OK_preValue;       // uint8_t 对应 byte
-
-            // 11(上次结果)数字区域的xmin、ymin、xmax、ymax坐标值
+            //04(配置信息)指针个数
+            public byte NUM_INDICATORS;       // uint8_t 对应 byte
+            //05 指针的尾数单位
+            public uint UNIT_OF_THE_LAST_INDICATOR; // uint32_t 对应 uint
+
+            //06 当前时间距离上次准确识别时间内的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
+            public ulong MAX_FLOW_THRESHOLD;  // uint64_t 对应 ulong           
+          
+            //07 8字节整数,表示识别结果,最小单位分升,即1dL=0.1L=1m³/10000),万分之一立方;
+            public ulong AI_RESULT;           // uint64_t 对应 ulong
+
+            //08 完整的结果
+            public ulong AI_COMPLETE_RESULT;
+
+            //09 数字区域的四个顶点坐标值(4组)
+            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+            public ushort[] CONFIG_NUM_REGION;       // uint16_t 对应 ushort, 8 个元素的数组
+            //10 首位指针坐标值(2组)
             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-            public ushort[] num_region;       // uint16_t 对应 ushort, 4 个元素的数组
-
-            // 12 是否需要上传整图?0=不需要;1=需要;
-            public byte is_get_whole_image;   // uint8_t 对应 byte
-
-            // 上传日志的printf回调函数
-            public IntPtr printfAiLog;        // 回调函数指针, 用 IntPtr 表示
-
-            // 14(配置信息)公司平台上传的图片的存储大小?(1:BMP彩色图; >1:JPG彩色图)
-            public ushort PingTai_image_size; // uint16_t 对应 ushort
-
-            // 15(配置信息)上传图像尺寸大小 = 数字区域区域外扩 PingTai_image_margin 像素
-            public byte PingTai_image_margin; // uint8_t 对应 byte
-
-            // 16(配置信息)数字区域的xmin、ymin、xmax、ymax坐标值
+            public ushort[] CONFIG_TWO_IND;       // uint16_t 对应 ushort, 4 个元素的数组
+            //11 表盘坐标
             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-            public ushort[] config_num_region; // uint16_t 对应 ushort, 4 个元素的数组
-
-            // 17(配置信息)图像倾斜角度
-            public short config_lean_angle;   // int16_t 对应 short
-
-            // 18(配置信息)在识别过程中,是否使用 config_num_region、config_lean_angle变量值
-            public byte is_use_config_info;   // uint8_t 对应 byte
-
-            // 19(配置信息)是否上传CNN模型的检测结果到服务器?(0=不上传;1=上传)
-            public byte is_upload_cnn_output; // uint8_t 对应 byte
-
-            // 20(配置信息)对CNN输出进行解码时,使用的NMS阈值(默认值=10)
-            public byte nms_threshold;        // uint8_t 对应 byte
-
-            // 21(配置信息)是否使用检测到的大框来排除数字(0=不用;1=用)(默认值=1)
-            public byte is_use_detected_num88;// uint8_t 对应 byte
-
-            // 22(上次结果),存储每个指针区域的xmin、ymin、xmax、ymax坐标值。最多存储10个指针
-            //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
-            //public ushort[,] indicators_regions; // uint16_t[10][4] 对应 ushort[10, 4]
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
-            public ushort[] indicators_regions; // 对应 uint16_t[10][4] 展开成一维数组
-
-            // 图片亮度
-            public float bright_factor;       // float 对应 float
-
-            // 仪表类型,模型类型
-            public byte meter_type;           // uint8_t 对应 byte
-
-            // 人工配置的水表参数使能开关
-            public byte manual_parameter_enable; // uint8_t 对应 byte
-
-            // 大图JPG图像的最大字节数
-            public ushort big_jpg_max_size;   // uint16_t 对应 ushort
-
-            // AI输出JPG图是,使用测试图标记,1:使用,0:不使用
-            public byte use_test_jpg_flag;    // uint8_t 对应 byte
+            public ushort[] CONFIG_METER_REGION;       // uint16_t 对应 ushort, 4 个元素的数组
 
-            // 表具中心点x
-            public float center_x;            // float 对应 float
+            //12 图片亮度
+            public float BRIGHT_FACTOR;       // float 对应 float
 
-            // 表具中心点y
-            public float center_y;            // float 对应 float
+            //13 大图JPG图像的最大字节数
+            public ushort BIG_JPG_SIZE;   // uint16_t 对应 ushort
 
-            // 数字水表图像放大倍率,中心点为图像中心
-            public float number_image_zoomrate;   // float 对应 float
+            //14 AI输出JPG图是,使用测试图标记,1:使用,0:不使用
+            public byte USE_TEST_JPG_FLAG;    // uint8_t 对应 byte
 
-            // 指针水表图像放大倍率,中心点为拟合圆圆心
-            public float indicator_image_zommrate;// float 对应 float
+            //15 压缩JPG的索引,来自after_struct,首次索引为0
+            public byte COMPRESS_JPG_INDEX;   // uint8_t 对应 byte
 
-            public float image_zoomrate;//图像放大倍率,中心点为拟合圆圆心
-
-            // 水表中数字区域位于上半部?1=位于上半部分;0=位于下半部分。 默认值为1
-            public byte is_nums_in_upper;     // uint8_t 对应 byte
-
-            // 数字+指针水表中“数字”部分读数;全指针水表中“黑色”指针部分的读数;全数字水表中“数字”部分读数
-            public ulong result_of_1st_part;  // uint64_t 对应 ulong
-
-            // 数字+指针水表中“指针”部分读数;全指针水表中“红色”指针部分的读数;用来判断此水表是否在动
-            public ulong result_of_2st_part;  // uint64_t 对应 ulong
-
-            // 压缩JPG的索引,来自after_struct,首次索引为0
-            public byte compress_jpg_index;   // uint8_t 对应 byte
-
-            // 是否上传红色指针部分的数据(0:不上传;1:上传;默认1)
-            public byte is_upload_red_ind_part;// uint8_t 对应 byte
-
-
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)]
-            public ulong[] history_result; // 对应 uint16_t[10][4] 展开成一维数组
-
-
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
-            public ulong[] history_digit_statistics;
+            //16 是否上传红色指针部分的数据(0:不上传;1:上传;默认1)
+            public byte IS_UPLOAD_RED_IND_PART;// uint8_t 对应 byte
+            
+            //17 上传日志的printf回调函数
+            public IntPtr printfAiLog;        // 回调函数指针, 用 IntPtr 表示  
         }
 
 
             //After结构体
-            [StructLayout(LayoutKind.Sequential, Pack = 1)]
+        [StructLayout(LayoutKind.Sequential, Pack = 1)]
         public struct AfterAI
         {
-            public byte NUM_DETECTED;    //01(本次结果)检测到的数字个数(黑色+红色)
-            public byte IND_DETECTED;    //01(本次结果)在数字+指针水表中,检测到的指针个数,全指针水表不用此值(2024.05.06增加)
-            public byte NUM_BLACK_DETECTED; //01 (本次信息)检测到的 数字个数(黑色)
+            public byte METER_TYPE;             //仪表类型(0:不是水表图片,1:数字+指针,2:全指针 3:全数字)
 
-            public ulong AI_result;      //识别最终结果,经过校正步骤后的最终结果,单位分升dL
-            public byte is_OK_preValue;  //ai_result的值是否是可信的值, add by djs 20240417
-            public ulong init_AI_result; //网络直接输出的、没有经过校正的最初识别结果,单位分升dL
+            public ulong AI_RESULT;             //识别最终结果,经过校正步骤后的最终结果,单位分升dL
+            public ulong INIT_AI_RESULT;        //网络直接输出的、没有经过校正的最初识别结果,单位分升dL
+            public ulong AI_COMPLETE_RESULT;    //最终的、完整的、经过校正步骤的结果,单位分升dL
 
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] // AI_result_array[2][12] 转成一维数组
-            public byte[] AI_result_array;    //03(本次结果)int数组的识别结果(2维数组,第一行元素为类别、第二行为识别概率)
+            public byte IS_VALUE_CHANGED;       //与上一次比较,水表读数是否在变化?变化范围包括数字+指针部分,也就是比较上次和本次的 AI_COMPLETE_RESULT
 
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-            public ushort[] num_region;    //04(本次结果)数字区域的xmin、ymin、xmax、ymax坐标值
-
-            public short lean_angle;    //05(本次结果)图像倾斜角度,和Before_AI_struct_RX结构体中的brightness一起维护图像倾斜角度
-
-            public byte is_get_ShuiLiSuo_image;  //06(本次结果)20220620 new add by lyj 是否生成了水利所的小图, 0=没有生产 1=生成了
-            public byte is_get_KaoJiPingTai_image; //07(本次结果)20220620 new add by lyj 是否生成了拷机平台的中图,0=没有生成 1=生成了
+            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] // AI_result_array[2][12] 转成一维数组
+            public byte[] AI_RESULT_ARRAY;    //03(本次结果)int数组的识别结果(2维数组,第一行元素为类别、第二行为识别概率)
 
-            public byte is_get_whole_image; //08(本次结果)是否生成了上传的整图?0=没有生成;1=生成了
-                                            //生成大图的条件:邓金生要求生成、或廉永健识别失败0.91、0.92
             [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
-            public byte[] AI_ver;  //由日期改为->类型加版本号
-
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1200)] // CNN_output_array[200][6] 转成一维数组
-            public ushort[] CNN_output_array; //10(本次结果)CNN网络的输出数据
-
-            public byte num_conf_valid_target; //11(本次结果)CNN网络的输出数据中有效目标的个数
-
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)] // indicators_regions[10][4] 转成一维数组
-            public ushort[] indicators_regions;    //12(本次结果)存储每个指针区域的xmin、ymin、xmax、ymax坐标值
-
-            public ulong MAX_FLOW_PER_HOUR;   //每小时的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
-            public ulong MAX_FLOW_threshold;  //当前时间距离上次准确识别时间内的最大流量,最小单位0.1L(1m³/10000), (万分之一立方)
-            public uint UNIT_OF_THE_LAST_NUMBER;   //尾数单位的含义,最小单位1分升,1dL, 0.1L(万分之一立方)
-
-            public float center_x;          //表具中心点x
-            public float center_y;          //表具中心点y
-            public float image_zoomrate;       //图像放大倍率,中心点为拟合圆圆心 
-            public float bright_factor; //图片亮度
-            public byte meter_type;    //仪表类型(0:不是水表图片,1:数字+指针,2:全指针 3:全数字)
-
-            public ulong result_of_1st_part;  //数字+指针水表中“数字”部分读数;全指针水表中“黑色”指针部分的读数;全数字水表中“数字”部分读数
-            public ulong result_of_2st_part;  //数字+指针水表中“指针”部分读数;全指针水表中“红色”指针部分的读数;全数字水表中“数字”部分读数
-            public ulong offset_1st_of_result; //暂时不用,数字+指针水表中“数字”部分的变化量;全指针水表中“黑色”指针部分的变化量
-            public ulong offset_2nd_of_result; //数字+指针水表中“指针”部分的变化量绝对值;全指针水表中“红色”指针部分的变化量
-            public byte compress_jpg_index;    //本次压缩的使用的索引
-
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
-            public ulong[] history_digit_statistics;
-
-            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
-            public ulong[] predicate_value;
+            public byte[] AI_VER;  //AI程序的数字版本号
 
-            public byte is_clean_history;
+            public byte COMPRESS_JPG_INDEX;    //上一次JPG的压缩索引
         }
 
         [StructLayout(LayoutKind.Sequential, Pack = 1)]

+ 187 - 36
MeterVision/FreeAi/FaRun.cs

@@ -78,19 +78,185 @@ namespace MeterVision.FreeAi
         //}
 
         public ResultModel StartRecognition(string jpgFile)
+        {
+            //PrintfCallback callback = PrintfCallbackMethod;
+
+            ////string jpgFile = resultModel.SrcImage;
+            ////判断图像尺寸是否合规
+            //if (!ThisApp.IsImageDimensionsValid(jpgFile))
+            //{
+            //    Console.WriteLine($"{jpgFile}图像尺寸不合规!");
+            //    return null;
+            //}
+
+
+            //short[] rgb565 = GetRGB565Array(jpgFile);
+
+            //if (rgb565.Length != (320 * 240))
+            //{
+            //    throw new InvalidOperationException("无效的图像大小。");
+            //}
+
+            //BeforeAI beforeAI = FaBefore.getBeforeAI();
+            //AfterAI afterAI = new AfterAI();
+            //SmallImage smallJpg = new SmallImage();
+            //BigImage bigJpg = new BigImage();
+
+            ////string onnxPath = ConfigItem.GetConfigItem().OnnxPath;
+            //string onnxPath = CfginiItem.GetConfigItem().OnnxPath;
+            //onnxPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, onnxPath);
+
+            ////判断onnxPath的路径是否存在
+            //if (!File.Exists(onnxPath))
+            //{
+            //    throw new InvalidOperationException($"onnx文件{onnxPath}不存在!");             
+            //}
+
+            ////调用识别
+            ////开始调用c_dll的识别函数
+            ////int result;
+
+            ////try
+            ////{
+            ////   result = recognition(rgb565, ref beforeAI, ref afterAI, ref smallJpg, ref bigJpg, onnxPath, callback);
+            ////}
+            ////catch(AccessViolationException ex)
+            ////{
+            ////    // 处理异常,例如记录日志
+            ////    Console.WriteLine("Access violation: " + ex.Message);
+            ////}
+
+
+            ////调用动态加载的方式
+            ////初始化加载动态库,
+
+            ////string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigItem.GetConfigItem().AiDll);
+            //string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CfginiItem.GetConfigItem().AiDll);
+
+            //if (!File.Exists(dllPath))
+            //{
+            //    throw new InvalidOperationException($"AI动态库文件{dllPath}不存在!");
+            //}
+
+            //int result = -1;
+            //if (DynamicFaImport.Initialize(dllPath))
+            //{
+            //    try
+            //    {
+            //        result = DynamicFaImport.Recognition(rgb565, ref beforeAI, ref afterAI, ref smallJpg, ref bigJpg, onnxPath, callback);
+
+            //    }
+            //    catch (AccessViolationException ex)
+            //    {
+            //        // 处理异常,例如记录日志
+            //        Console.WriteLine("Access violation: " + ex.Message);
+            //    }
+            //    catch (Exception ex)
+            //    {
+            //        Console.WriteLine("Exeception: " + ex.Message);
+            //    }
+            //}
+            //else
+            //{
+            //    throw new InvalidOperationException($"初始化AI动态库{dllPath}失败!");
+            //}
+            //DynamicFaImport.Unload();
+
+
+            ////存储图像
+            //string dstImagPath = CfginiItem.GetConfigItem().DstImgPath;
+            //dstImagPath = Path.Combine(dstImagPath, ThisApp.GetNowTime_yyyyMMdd());
+            //if (!Directory.Exists(dstImagPath))
+            //{
+            //    Directory.CreateDirectory(dstImagPath);
+            //}
+            //dstImagPath = Path.Combine(dstImagPath, Guid.NewGuid().ToString() + ".jpg");
+            //SaveBigJpg(bigJpg, dstImagPath);
+
+
+            ////存储日志
+            //string aiLogPath = CfginiItem.GetConfigItem().AiLogPath;
+            //aiLogPath = Path.Combine(aiLogPath, ThisApp.GetNowTime_yyyyMMdd());
+            //if (!Directory.Exists(aiLogPath))
+            //{
+            //    Directory.CreateDirectory(aiLogPath);
+            //}
+            //aiLogPath = Path.Combine(aiLogPath, Guid.NewGuid().ToString() + ".txt");
+
+            //try
+            //{
+            //    if (LicenseMana.mLicenseModel != null && LicenseMana.mLicenseModel.IsPermanent)
+            //    {
+            //        File.WriteAllText(aiLogPath, mRunLog.ToString());
+            //    }
+            //    else
+            //    {
+            //        File.WriteAllText(aiLogPath, "");
+            //    }
+            //}
+            //catch(Exception ex)
+            //{
+            //    throw new InvalidOperationException($"保存文件{aiLogPath}失败:{ex.Message}");
+            //}
+
+            ////结果对象赋值
+            //ResultModel resultModel = new ResultModel();
+            //resultModel.SrcImage = jpgFile;
+            //resultModel.DstImage = dstImagPath;
+            //resultModel.LogPath = aiLogPath;
+
+            //resultModel.MeterType = afterAI.meter_type;
+
+            //if(afterAI.meter_type == 1)
+            //{
+            //    //数字水表: 数字+指针
+            //    resultModel.DigitCount = afterAI.NUM_DETECTED;
+            //    resultModel.PointerCount = afterAI.IND_DETECTED;
+            //}
+            //else if(afterAI.meter_type == 2)
+            //{
+            //    //全指针
+            //    resultModel.DigitCount = 0;
+            //    resultModel.PointerCount = afterAI.NUM_DETECTED;
+            //}else if(afterAI.meter_type == 3)
+            //{
+            //    //全数字
+            //    resultModel.DigitCount = afterAI.NUM_DETECTED;
+            //    resultModel.PointerCount = 0;
+            //}
+            //else
+            //{
+            //    resultModel.MeterType = 0;
+            //    resultModel.DigitCount = 0;
+            //    resultModel.PointerCount = 0;
+            //}
+            ////1,2,7,8
+            //resultModel.ResultType = afterAI.AI_result_array[11];
+            //resultModel.RawValue = afterAI.init_AI_result;
+            //resultModel.FinalValue = afterAI.AI_result;
+            //resultModel.LastUnit = afterAI.UNIT_OF_THE_LAST_NUMBER;
+            //resultModel.AiVer = BitConverter.ToString(afterAI.AI_ver).Replace("-", "");
+            //resultModel.DebugInfoBytes = afterAI.AI_result_array;
+
+            //return resultModel;            
+            return null;
+        }
+
+
+        public ResultModel StartRecognition(SingleDetailItem detailItem)
         {
             PrintfCallback callback = PrintfCallbackMethod;
 
             //string jpgFile = resultModel.SrcImage;
             //判断图像尺寸是否合规
-            if (!ThisApp.IsImageDimensionsValid(jpgFile))
+            if (!ThisApp.IsImageDimensionsValid(detailItem.SrcImage))
             {
-                Console.WriteLine($"{jpgFile}图像尺寸不合规!");
+                Console.WriteLine($"{detailItem.SrcImage}图像尺寸不合规!");
                 return null;
             }
 
 
-            short[] rgb565 = GetRGB565Array(jpgFile);
+            short[] rgb565 = GetRGB565Array(detailItem.SrcImage);
 
             if (rgb565.Length != (320 * 240))
             {
@@ -105,11 +271,11 @@ namespace MeterVision.FreeAi
             //string onnxPath = ConfigItem.GetConfigItem().OnnxPath;
             string onnxPath = CfginiItem.GetConfigItem().OnnxPath;
             onnxPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, onnxPath);
-            
+
             //判断onnxPath的路径是否存在
             if (!File.Exists(onnxPath))
             {
-                throw new InvalidOperationException($"onnx文件{onnxPath}不存在!");             
+                throw new InvalidOperationException($"onnx文件{onnxPath}不存在!");
             }
 
             //调用识别
@@ -194,53 +360,38 @@ namespace MeterVision.FreeAi
                     File.WriteAllText(aiLogPath, "");
                 }
             }
-            catch(Exception ex)
+            catch (Exception ex)
             {
                 throw new InvalidOperationException($"保存文件{aiLogPath}失败:{ex.Message}");
             }
 
             //结果对象赋值
             ResultModel resultModel = new ResultModel();
-            resultModel.SrcImage = jpgFile;
+            resultModel.SrcImage = detailItem.SrcImage;
             resultModel.DstImage = dstImagPath;
             resultModel.LogPath = aiLogPath;
 
-            resultModel.MeterType = afterAI.meter_type;
-
-            if(afterAI.meter_type == 1)
-            {
-                //数字水表: 数字+指针
-                resultModel.DigitCount = afterAI.NUM_DETECTED;
-                resultModel.PointerCount = afterAI.IND_DETECTED;
-            }
-            else if(afterAI.meter_type == 2)
-            {
-                //全指针
-                resultModel.DigitCount = 0;
-                resultModel.PointerCount = afterAI.NUM_DETECTED;
-            }else if(afterAI.meter_type == 3)
-            {
-                //全数字
-                resultModel.DigitCount = afterAI.NUM_DETECTED;
-                resultModel.PointerCount = 0;
+            resultModel.MeterType = afterAI.METER_TYPE;
+            //1,2,7,8
+            resultModel.ResultType = afterAI.AI_RESULT_ARRAY[11];
+            resultModel.RawValue = afterAI.INIT_AI_RESULT;
+            resultModel.FinalValue = afterAI.AI_RESULT;
+            
+            if (detailItem.MeterType == 1 || detailItem.MeterType == 3) {
+                resultModel.LastUnit = beforeAI.UNIT_OF_THE_LAST_NUMBER;
             }
-            else
+            else if(detailItem.MeterType == 2)
             {
-                resultModel.MeterType = 0;
-                resultModel.DigitCount = 0;
-                resultModel.PointerCount = 0;
+                resultModel.LastUnit = beforeAI.UNIT_OF_THE_LAST_INDICATOR;
             }
-            //1,2,7,8
-            resultModel.ResultType = afterAI.AI_result_array[11];
-            resultModel.RawValue = afterAI.init_AI_result;
-            resultModel.FinalValue = afterAI.AI_result;
-            resultModel.LastUnit = afterAI.UNIT_OF_THE_LAST_NUMBER;
-            resultModel.AiVer = BitConverter.ToString(afterAI.AI_ver).Replace("-", "");
-            resultModel.DebugInfoBytes = afterAI.AI_result_array;
 
+            resultModel.AiVer = BitConverter.ToString(afterAI.AI_VER).Replace("-", "");
+            resultModel.DebugInfoBytes = afterAI.AI_RESULT_ARRAY;
+            
             return resultModel;
         }
 
+
         private short[] GetRGB565Array(string jpgFilePath)
         {
             using (Bitmap bmp = new Bitmap(jpgFilePath))

+ 4 - 4
MeterVision/MainWindow.xaml

@@ -37,18 +37,18 @@
 
                 <TextBlock Width="1" Background="#686868" Margin="5 5 5 5" Visibility="Visible" />
 
-                <Button x:Name="btnSetStation" BorderThickness="0" Click="BtnSetStation_Click" Background="Transparent" Margin="5 0 5 0" BorderBrush="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center">
+                <Button x:Name="btnSetStation" Visibility="Collapsed" BorderThickness="0" Click="BtnSetStation_Click" Background="Transparent" Margin="5 0 5 0" BorderBrush="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center">
                     <Button.Content>
                         <StackPanel Orientation="Horizontal">
-                            <!-- 图标 -->
+                             <!--图标--> 
                             <Image Source="Assest/set_station.png" Width="16" Height="16" Margin="0,0,5,0" />
-                            <!-- 文字 -->
+                             <!--文字--> 
                             <TextBlock x:Name="txtSetStation" FontSize="14" FontWeight="Normal" Text="站点设置" Foreground="Black" VerticalAlignment="Center" />
                         </StackPanel>
                     </Button.Content>
                 </Button>
 
-                <TextBlock Width="1" Background="#686868" Margin="5 5 5 5" Visibility="Visible" />
+                <TextBlock Width="1" Background="#686868" Margin="5 5 5 5" Visibility="Collapsed" />
 
                 <Button x:Name="btnSingleImage" BorderThickness="0" Click="BtnSingleImage_Click" Background="Transparent" Margin="5 0 5 0" BorderBrush="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center">
                     <Button.Content>

+ 343 - 0
MeterVision/Mark/MarkManager.cs

@@ -0,0 +1,343 @@
+using System;
+using System.Collections.Generic;
+//using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Shapes;
+
+namespace MeterVision.Mark
+{
+    //水表图片标注管理
+    public class MarkManager
+    {
+        private readonly Brush[] _brushes = new Brush[] { Brushes.Red, Brushes.Orange, Brushes.Yellow, Brushes.Green };
+        //画布
+        private Canvas _canvas;
+        //最多4个点
+        private List<Point> _points;
+        //用来保存已绘制的图像
+        private List<UIElement> _drawShapes;
+        //要求的画点数(只有2、3、4三种情况)
+        //1. 2个点取指针表第一个指针与最后一个指针的直线
+        //2. 3个点取数字区域的选取
+        //3. 4个点表盘区域的选取
+        public int PointCount { get; private set; }
+        //public int _curPointCount = 0;
+
+        public bool IsMarking { get; private set; }   //正在标注标记
+
+        //标注完成事件
+        public event Action<int, List<Point>, int> FinishMark;
+
+        public event Action<string> MarkError;
+
+
+        public MarkManager(Canvas canvas)
+        {
+            _canvas = canvas;
+            _points = new List<Point>();
+            _drawShapes = new List<UIElement>();
+            PointCount = 0;
+            IsMarking = false;
+        }
+
+        //开始标注
+        public void ReadyMark(int pointCount)
+        {
+            //删除所有绘制的形状,不能删除表盘图像
+            foreach (var shape in _drawShapes)
+            {
+                _canvas.Children.Remove(shape);
+            }
+            _drawShapes.Clear();
+            _points.Clear();
+            PointCount = pointCount;
+            IsMarking = true;
+            //_curPointCount = 0;
+        }
+
+        //清除标注
+        public void ClearMark()
+        {
+            ReadyMark(PointCount);
+            //IsMarking = false;
+        }
+
+        public void InitMark()
+        {
+            ReadyMark(0);
+            IsMarking = false;
+        }
+
+
+        public void StopMark()
+        {
+            IsMarking = false;
+        }
+
+        private void AddPoint(Point point)
+        {
+            int index = _points.Count;
+            // 绘制点击点
+            Ellipse pointEllipse = new Ellipse
+            {
+                Width = 5,
+                Height = 5,
+                Fill = _brushes[index]
+            };
+            Canvas.SetLeft(pointEllipse, point.X - 2.5); // 调整点的中心位置
+            Canvas.SetTop(pointEllipse, point.Y - 2.5);
+            _canvas.Children.Add(pointEllipse);
+            _drawShapes.Add(pointEllipse);
+
+            _points.Add(point);
+        }
+
+        //更新指定位置的点坐标及标注
+        private void UpdatePoint(Point point, int position)
+        {
+            int index = _canvas.Children.IndexOf(_drawShapes[position - 1]);
+            _canvas.Children.RemoveAt(index);
+
+            Ellipse pointEllipse = new Ellipse
+            {
+                Width = 5,
+                Height = 5,
+                Fill = _brushes[position - 1]
+            };
+
+            Canvas.SetLeft(pointEllipse, point.X - 2.5); // 调整点的中心位置
+            Canvas.SetTop(pointEllipse, point.Y - 2.5);
+            _canvas.Children.Insert(index, pointEllipse);
+
+            _drawShapes.RemoveAt(position - 1);
+            _drawShapes.Insert(position - 1, pointEllipse);
+
+            _points.RemoveAt(position - 1);
+            _points.Insert(position - 1, point);
+        }
+
+        //点标注
+        public void PointMark(Point point)
+        {
+            if (IsMarking && _points.Count < PointCount)
+            {
+                AddPoint(point);
+
+                if (_points.Count == PointCount)
+                {
+                    if (PointCount == 4)
+                    {
+                        //四点标表盘
+                        FourPoint_DrawRectangle();
+                    }
+                    else if (PointCount == 3)
+                    {
+                        //三点画矩形
+                        ThreePoint_DrawRectangle();
+                    }
+                    else if (PointCount == 2)
+                    {
+                        TwoPoint_DrawLine();
+                    }
+                }//if _points.Count == PointCount
+            }//if IsMarking
+        }
+
+        // 判断三点是否共线
+        private bool ArePointsCollinear(Point p1, Point p2, Point p3)
+        {
+            // 使用叉积判断三点是否共线
+            double crossProduct = (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X);
+            return Math.Abs(crossProduct) < 0.0001;
+        }
+
+        //计算L12与L23的交点,作为新的第二个点
+        private Point CalculatePerpendicularIntersection(Point p1, Point p2, Point p3)
+        {
+            double x1 = p1.X, y1 = p1.Y;
+            double x2 = p2.X, y2 = p2.Y;
+            double x3 = p3.X, y3 = p3.Y;
+
+            // 计算L12的斜率
+            double slopeL23 = (y3 - y2) / (x3 - x2);
+            // 计算L23的斜率,L23垂直于L12,所以斜率为 -1 / slopeL12
+            double slopeL12 = -1 / slopeL23;
+
+            // L12的方程为:y = slopeL12 * x + b1
+            double b1 = y1 - slopeL12 * x1;
+
+            // L23的方程为:y = slopeL23 * x + b2
+            double b2 = y3 - slopeL23 * x3;
+
+            // 计算L12与L23的交点
+            double intersectX = (b2 - b1) / (slopeL12 - slopeL23);
+            double intersectY = slopeL12 * intersectX + b1;
+
+            return new Point(intersectX, intersectY);
+        }
+
+        //计算矩形的第四个点
+        private Point CalculateFourthPoint(Point p1, Point p2, Point p3)
+        {
+            // 计算从P1到P2的向量
+            double dx1 = p2.X - p1.X;
+            double dy1 = p2.Y - p1.Y;
+
+            // 计算从P1到P3的向量
+            double dx2 = p3.X - p1.X;
+            double dy2 = p3.Y - p1.Y;
+
+            // 通过平行四边形法则计算第四个点
+            double x4 = p3.X - dx1;
+            double y4 = p3.Y - dy1;
+
+            return new Point(x4, y4);
+        }
+
+        // 绘制旋转矩形,连接四个顶点
+        private void DrawRotatedRectangle(Point p1, Point p2, Point p3, Point p4)
+        {
+            // 绘制矩形的四条边
+            Line line1 = new Line
+            {
+                X1 = p1.X,
+                Y1 = p1.Y,
+                X2 = p2.X,
+                Y2 = p2.Y,
+                Stroke = Brushes.Blue,
+                StrokeThickness = 2
+            };
+
+            Line line2 = new Line
+            {
+                X1 = p2.X,
+                Y1 = p2.Y,
+                X2 = p3.X,
+                Y2 = p3.Y,
+                Stroke = Brushes.Blue,
+                StrokeThickness = 2
+            };
+
+            Line line3 = new Line
+            {
+                X1 = p3.X,
+                Y1 = p3.Y,
+                X2 = p4.X,
+                Y2 = p4.Y,
+                Stroke = Brushes.Blue,
+                StrokeThickness = 2
+            };
+
+            Line line4 = new Line
+            {
+                X1 = p4.X,
+                Y1 = p4.Y,
+                X2 = p1.X,
+                Y2 = p1.Y,
+                Stroke = Brushes.Blue,
+                StrokeThickness = 2
+            };
+
+            _canvas.Children.Add(line1);
+            _canvas.Children.Add(line2);
+            _canvas.Children.Add(line3);
+            _canvas.Children.Add(line4);
+            _drawShapes.Add(line1);
+            _drawShapes.Add(line2);
+            _drawShapes.Add(line3);
+            _drawShapes.Add(line4);
+        }
+
+        //三点画矩形
+        private void ThreePoint_DrawRectangle()
+        {
+            //判断是否为三点共线
+            if (ArePointsCollinear(_points[0], _points[1], _points[2]))
+            {
+                ReadyMark(PointCount);
+                MarkError?.Invoke("三个点共线,无效的标注");
+                return;
+            }
+
+            //计算新的第二个点
+            Point newSecondPoint = CalculatePerpendicularIntersection(_points[0], _points[1], _points[2]);
+            UpdatePoint(newSecondPoint, 2); //第2个位置更新
+            //AddPoint(newSecondPoint);
+
+
+            // 计算第四个点(矩形的另一个角)
+            Point fourthPoint = CalculateFourthPoint(_points[0], newSecondPoint, _points[2]);
+            AddPoint(fourthPoint);  //增加第四个位置
+
+
+            DrawRotatedRectangle(_points[0], newSecondPoint, _points[2], fourthPoint);
+
+            IsMarking = false;
+            FinishMark?.Invoke(3, _points, 0);
+        }
+
+        //四点标记表盘
+        private void FourPoint_DrawRectangle()
+        {
+            //通过标记的四个点来计算矩形的坐标
+            double xMin = Math.Min(Math.Min(_points[0].X, _points[1].X), Math.Min(_points[2].X, _points[3].X));
+            double yMin = Math.Min(Math.Min(_points[0].Y, _points[1].Y), Math.Min(_points[2].Y, _points[3].Y));
+
+            double xMax = Math.Max(Math.Max(_points[0].X, _points[1].X), Math.Max(_points[2].X, _points[3].X));
+            double yMax = Math.Max(Math.Max(_points[0].Y, _points[1].Y), Math.Max(_points[2].Y, _points[3].Y));
+
+            double width = Math.Abs(xMax - xMin);
+            double height = Math.Abs(yMax - yMin);
+
+            //画矩形
+            var rectangle = new Rectangle
+            {
+                Stroke = Brushes.Blue,
+                StrokeThickness = 2,
+                Fill = Brushes.Transparent,
+                Width = width,
+                Height = height
+            };
+
+            _canvas.Children.Add(rectangle);
+            Canvas.SetLeft(rectangle, xMin);
+            Canvas.SetTop(rectangle, yMin);
+
+            _drawShapes.Add(rectangle);
+
+            List<Point> points = new List<Point>
+            {
+                new Point(xMin,yMin),new Point(xMax,yMax)
+            };
+
+
+            IsMarking = false;
+            FinishMark?.Invoke(2, points, 0);
+        }
+
+        //2点标记直线
+        private void TwoPoint_DrawLine()
+        {
+            Line line = new Line
+            {
+                X1 = _points[0].X,
+                Y1 = _points[0].Y,
+                X2 = _points[1].X,
+                Y2 = _points[1].Y,
+                Stroke = Brushes.Blue,
+                StrokeThickness = 2
+            };
+            _canvas.Children.Add(line);
+            _drawShapes.Add(line);
+
+            IsMarking = false;
+            FinishMark?.Invoke(3, _points, 0);
+        }
+        //-------------------------------------------------------------------------
+    }
+}

+ 58 - 0
MeterVision/Mark/UCMark.xaml

@@ -0,0 +1,58 @@
+<UserControl x:Class="MeterVision.Mark.UCMark"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:MeterVision.Mark"
+             mc:Ignorable="d" 
+             Background="White"
+             d:DesignHeight="450" d:DesignWidth="600">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="30" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+
+        <Border Grid.Row="0" BorderThickness="0 0 0 1" BorderBrush="#ACAAAA" Background="AliceBlue" Padding="10 0 0 0">
+            <Grid>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="Auto" />
+                    <ColumnDefinition Width="*" />
+                    <ColumnDefinition Width="Auto" />
+                </Grid.ColumnDefinitions>
+
+                <StackPanel Orientation="Horizontal" Grid.Column="0" Background="Transparent">
+                    <Button x:Name="btnMeterRegion" Content="标注表盘" Foreground="Black" FontSize="14px" 
+                        Click="BtnMeterRegion_Click" Background="Transparent" BorderThickness="0" />
+                    <TextBlock Width="1" Background="#686868" Margin="5 5 5 5" Visibility="Visible" />
+                    <Button x:Name="btnFeatureRegion" Content="标注特征区域" FontSize="14px" 
+                        Click="BtnFeatureRegion_Click" Background="Transparent" BorderThickness="0" />
+                    <TextBlock x:Name="txtFeatureRegion" Width="1" Background="#686868" Margin="5 5 5 5" Visibility="Visible" />
+                    <Button x:Name="btnClearMark" Content="清除标注" FontSize="14px" 
+                            Click="BtnClearMark_Click" Background="Transparent" BorderThickness="0"/>
+                </StackPanel>
+                <TextBlock x:Name="txtCurPoint" Grid.Column="2" Text="坐标: x,y" Width="200"  FontSize="14px" VerticalAlignment="Center"  />
+            </Grid>
+        </Border>
+
+        <Border x:Name="pnlCanvas" Grid.Row="1" BorderBrush="#ACAAAA" BorderThickness="0" Margin="5" Padding="0" 
+                SizeChanged="PnlCanvas_SizeChanged">
+            <Canvas Name="drawingCanvas"  HorizontalAlignment="Center" VerticalAlignment="Center"                        
+                    MouseDown="DrawingCanvas_MouseDown" MouseMove="DrawingCanvas_MouseMove"
+                    MouseUp="DrawingCanvas_MouseUp" MouseEnter="DrawingCanvas_MouseEnter"
+                    MouseLeave="DrawingCanvas_MouseLeave" Background="Transparent">
+                <Image Name="imgControl" Stretch="None" >
+                    <Image.RenderTransform>
+                        <ScaleTransform x:Name="scaleTransform" ScaleX="1.5" ScaleY="1.5" />
+                    </Image.RenderTransform>
+                </Image>
+                <!--<Rectangle Fill="#60FF0000" 
+                       Width="{Binding ActualWidth, ElementName=drawingCanvas}"
+                       Height="{Binding ActualHeight, ElementName=drawingCanvas}"
+                       Canvas.Left="{Binding (Canvas.Left), ElementName=drawingCanvas}"
+                       Canvas.Top="{Binding (Canvas.Top), ElementName=drawingCanvas}"/>-->
+            </Canvas>
+        </Border>
+
+    </Grid>
+</UserControl>

+ 341 - 0
MeterVision/Mark/UCMark.xaml.cs

@@ -0,0 +1,341 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+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.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace MeterVision.Mark
+{
+    /// <summary>
+    /// UCMark.xaml 的交互逻辑
+    /// </summary>
+    public partial class UCMark : UserControl
+    {
+        //当前的放大倍率
+        //private double _sacle_value = 1.5f;
+
+        //标注的类型: 4:表盘; 3:数字域; 2:首位指针; 0:未选中
+        private int _mark_type = 0;
+
+        //是否已加载图片
+        //private bool _loadedImage = false;
+
+        private MarkManager _markManager;
+
+        private int _meterType;
+        public int MeterType
+        {
+            private get => _meterType;
+            set
+            {
+                if(_meterType != value)
+                {
+                    _meterType = value;
+                    SetFeatureRegion(_meterType);
+                }
+            }
+        }
+
+        public event Action<string,int> MeterRegion_MarkFinished;
+        public event Action<string,int> FeatureRegion_MarkFinished;
+
+
+        BitmapImage bitmap;
+        public UCMark()
+        {
+            InitializeComponent();
+
+            _mark_type = 0;
+            _markManager = new MarkManager(drawingCanvas);
+            _markManager.MarkError += _markManager_MarkError;
+            _markManager.FinishMark += _markManager_FinishMark;
+
+            //初始化一些控件的默认显示值
+            txtCurPoint.Text = "";
+
+            this.Loaded += UCMark_Loaded;
+        }
+
+        private void UCMark_Loaded(object sender, RoutedEventArgs e)
+        {
+            //throw new NotImplementedException();
+            //ChangeScale();
+        }
+
+        private void PnlCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
+        {
+            ChangeScale();
+            if (_markManager != null)
+            {
+                _markManager.ClearMark();
+            }
+        }
+
+        public void SetImagePath(string imgPath)
+        {
+            //MeterType = meterType;
+            //加载照片
+            LoadImage(imgPath);
+            //设置放大倍率
+            //_sacle_value = 1.92f;
+            //SetScaleTrnsform(_sacle_value);
+        }
+
+        //private void SetScaleTrnsform(double scale)
+        //{
+        //    scaleTransform.ScaleX = scale;
+        //    scaleTransform.ScaleY = scale;
+        //    _markManager.ClearMark();
+        //}
+
+        private void LoadImage(string filePath)
+        {
+            // 创建一个BitmapImage对象,并加载图片
+            bitmap = new BitmapImage(new Uri(filePath));
+
+            if (bitmap.Width != 320 || bitmap.Height != 240)
+            {
+                MessageBox.Show(Application.Current.MainWindow, "图片尺寸必须为320*240!", "提示",
+                        MessageBoxButton.OK, MessageBoxImage.Information);
+                return;
+            }
+            imgControl.Source = bitmap; // 将图片显示到Image控件中
+
+            drawingCanvas.Width = bitmap.PixelWidth;
+            drawingCanvas.Height = bitmap.PixelHeight;
+
+            _markManager.InitMark();
+        }
+
+        private void ChangeScale()
+        {
+            // 获取图片原始尺寸
+            double imageWidth = bitmap.PixelWidth;
+            double imageHeight = bitmap.PixelHeight;
+
+            // 获取pnlCanvas的实际可用区域(考虑Margin和Padding)
+            double containerWidth = pnlCanvas.ActualWidth - pnlCanvas.Padding.Left - pnlCanvas.Padding.Right;
+            double containerHeight = pnlCanvas.ActualHeight - pnlCanvas.Padding.Top - pnlCanvas.Padding.Bottom;
+
+            // 计算缩放比例,留5%边距
+            double scaleX = (containerWidth * 0.95) / imageWidth;
+            double scaleY = (containerHeight * 0.95) / imageHeight;
+            double scale = Math.Min(scaleX, scaleY);
+
+            // 应用缩放
+            scaleTransform.ScaleX = scale;
+            scaleTransform.ScaleY = scale;
+
+            // 计算图片缩放后的尺寸
+            double scaledWidth = imageWidth * scale;
+            double scaledHeight = imageHeight * scale;
+
+            // 将图片居中于Canvas
+            //Canvas.SetLeft(imgControl, (containerWidth - scaledWidth) / 2);
+            //Canvas.SetTop(imgControl, (containerHeight - scaledHeight) / 2);
+
+            // 关键步骤:强制Canvas与Image的渲染区域一致
+            drawingCanvas.Width = scaledWidth;      //containerWidth;    // 使Canvas宽度与Border可用区域一致
+            drawingCanvas.Height = scaledHeight;     //containerHeight;   // 使Canvas高度与Border可用区域一致
+        }
+
+
+
+        private void _markManager_FinishMark(int pointCount, List<Point> points, int angle)
+        {
+            double scaleX = scaleTransform.ScaleX;
+            double scaleY = scaleTransform.ScaleY;
+
+            string regions = "";
+
+            if (_mark_type == 4)
+            {
+                //表盘标注完成(获取到2组坐标)
+                Point point1 = points[0];
+                Point point2 = points[1];
+                int xMin = (int)(point1.X / scaleX);
+                int yMin = (int)(point1.Y / scaleY);
+                int xMax = (int)(point2.X / scaleX);
+                int yMax = (int)(point2.Y / scaleY);
+                regions = $"{xMin},{yMin} {xMax},{yMax}";
+                MeterRegion_MarkFinished?.Invoke(regions,MeterType);
+            }
+            else if (_mark_type == 3)
+            {
+                //数字区域标注完成
+                regions = $"" +
+                    $"{(int)(points[0].X / scaleX)}," +
+                    $"{(int)(points[0].Y / scaleY)} " +
+                    $"{(int)(points[1].X / scaleX)}," +
+                    $"{(int)(points[1].Y / scaleY)} " +
+                    $"{(int)(points[2].X / scaleX)}," +
+                    $"{(int)(points[2].Y / scaleY)} " +
+                    $"{(int)(points[3].X / scaleX)}," +
+                    $"{(int)(points[3].Y / scaleY)}";
+                FeatureRegion_MarkFinished?.Invoke(regions,MeterType);
+            }
+            else if (_mark_type == 2)
+            {
+                //指针首位标注完成
+                regions = $"" +
+                    $"{(int)(points[0].X / scaleX)}," +
+                    $"{(int)(points[0].Y / scaleY)} " +
+                    $"{(int)(points[1].X / scaleX)}," +
+                    $"{(int)(points[1].Y / scaleY)}";
+                FeatureRegion_MarkFinished?.Invoke(regions, MeterType);
+            }
+        }
+
+        private void _markManager_MarkError(string message)
+        {
+            MessageBox.Show(message);
+        }
+
+        private void SetFeatureRegion(int meterType)
+        {
+            if(meterType == 1 || meterType == 3)
+            {
+                btnFeatureRegion.Visibility = txtFeatureRegion.Visibility = Visibility.Visible;
+                btnFeatureRegion.Content = "标注数据域(4点)";
+            }
+            else if(meterType == 2)
+            {
+                btnFeatureRegion.Visibility = txtFeatureRegion.Visibility = Visibility.Visible;
+                btnFeatureRegion.Content = "标注首位指针(2点)";
+            }
+            else
+            {
+                btnFeatureRegion.Visibility = txtFeatureRegion.Visibility = Visibility.Collapsed;
+            }
+
+        }
+
+        private void SetBtnMark(int mark_type)
+        {
+            btnMeterRegion.Foreground = btnFeatureRegion.Foreground = Brushes.Black;
+            if(mark_type == 4)
+            {
+                btnMeterRegion.Foreground = Brushes.Red;
+            }
+            else if(mark_type == 2 || mark_type == 3)
+            {
+                btnFeatureRegion.Foreground = Brushes.Red;
+            }
+        }
+
+        private void BtnMeterRegion_Click(object sender, RoutedEventArgs e)
+        {
+            if (MeterType <= 0) return;
+            if(_mark_type != 4)
+            {
+                _mark_type = 4;
+                _markManager.ReadyMark(_mark_type);
+            }
+            else
+            {
+                _mark_type = 0;
+                _markManager.StopMark();
+            }
+            SetBtnMark(_mark_type);
+        }
+
+        private void BtnFeatureRegion_Click(object sender, RoutedEventArgs e)
+        {          
+            if(MeterType == 1 || MeterType == 3)
+            {
+                //数字域标注
+                if(_mark_type != 3)
+                {
+                    _mark_type = 3;
+                    _markManager.ReadyMark(_mark_type);
+                }
+                else
+                {
+                    _mark_type = 0;
+                    _markManager.StopMark();
+                }
+            }
+            else if(MeterType == 2)
+            {
+                //首位指针标注
+                if(_mark_type != 2)
+                {
+                    _mark_type = 2;
+                    _markManager.ReadyMark(_mark_type);
+                }
+                else
+                {
+                    _mark_type = 2;
+                    _markManager.StopMark();
+                }
+            }
+            SetBtnMark(_mark_type);
+        }
+
+        private void BtnClearMark_Click(object sender, RoutedEventArgs e)
+        {
+            _markManager.ClearMark();
+        }
+
+        private void DrawingCanvas_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            if (_markManager.IsMarking && _mark_type > 0)
+            {
+                //获取点击位置
+                Point clickPoint = e.GetPosition(drawingCanvas);
+                _markManager.PointMark(clickPoint);
+            }
+        }
+
+        private void DrawingCanvas_MouseMove(object sender, MouseEventArgs e)
+        {
+            Point mousePos = e.GetPosition(drawingCanvas);
+
+            // 将鼠标位置转换为原图的坐标
+            double originalX = mousePos.X / scaleTransform.ScaleX;
+            double originalY = mousePos.Y / scaleTransform.ScaleY;
+
+            int mouseX = (int)originalX;
+            int mouseY = (int)originalY;
+
+
+            //this.Title = $"Mouse Position : {mouseX},{mouseY}";
+            txtCurPoint.Text = $"鼠标坐标: {mouseX},{mouseY}";
+        }
+
+        private void DrawingCanvas_MouseUp(object sender, MouseButtonEventArgs e)
+        {
+
+        }
+
+        private void DrawingCanvas_MouseEnter(object sender, MouseEventArgs e)
+        {
+            if (_mark_type > 0)
+            {
+                this.Cursor = Cursors.Hand;
+            }
+            else
+            {
+                this.Cursor = Cursors.Arrow;
+            }
+        }
+
+        private void DrawingCanvas_MouseLeave(object sender, MouseEventArgs e)
+        {
+            txtCurPoint.Text = "";
+            this.Cursor = Cursors.Arrow;
+        }
+
+
+        //-----------------------------------------------------------------------
+    }
+}

+ 15 - 0
MeterVision/MeterVision.csproj

@@ -169,6 +169,9 @@
     <Compile Include="Dlg\EditNameDlg.xaml.cs">
       <DependentUpon>EditNameDlg.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Dlg\EditSingleConfig.xaml.cs">
+      <DependentUpon>EditSingleConfig.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Dlg\EditStandValueDlg.xaml.cs">
       <DependentUpon>EditStandValueDlg.xaml</DependentUpon>
     </Compile>
@@ -206,6 +209,10 @@
     <Compile Include="ImageViewerWindow.xaml.cs">
       <DependentUpon>ImageViewerWindow.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Mark\MarkManager.cs" />
+    <Compile Include="Mark\UCMark.xaml.cs">
+      <DependentUpon>UCMark.xaml</DependentUpon>
+    </Compile>
     <Compile Include="model\CompDetailItem.cs" />
     <Compile Include="model\CompFindModel.cs" />
     <Compile Include="model\CompItem.cs" />
@@ -329,6 +336,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Dlg\EditSingleConfig.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Dlg\EditStandValueDlg.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
@@ -365,6 +376,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Mark\UCMark.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Patch\AddCompDlg.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 67 - 9
MeterVision/Single/UCSingleGrid.xaml

@@ -166,10 +166,14 @@
                                     <TextBlock Text="结果:" FontSize="13px" Foreground="#666666" Padding="5 0 0 0"/>
                                     <TextBlock Text="{Binding FinalValue}" FontSize="14px" Foreground="#000000" />
                                 </StackPanel>
-                                <StackPanel Orientation="Horizontal">
+                                <StackPanel Orientation="Horizontal" Margin="0 0 0 5">
                                     <TextBlock Text="原始:" FontSize="13px" Foreground="#666666" Padding="5 0 0 0" />
                                     <TextBlock Text="{Binding RawValue}" FontSize="14px" Foreground="#000000" />
                                 </StackPanel>
+                                <StackPanel Orientation="Horizontal">
+                                    <TextBlock Text="类型:" FontSize="13px" Foreground="#666666" Padding="5 0 0 0" />
+                                    <TextBlock Text="{Binding ResultMeter}" FontSize="14px" Foreground="#000000" />
+                                </StackPanel>
                             </StackPanel>
                         </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
@@ -277,13 +281,16 @@
                                     <TextBlock Text="{Binding AI_result_array[23]}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#333333" FontSize="13px"/>
                                 </Border>
 
-                                <StackPanel Grid.Row="2" Grid.ColumnSpan="12">
+                                <StackPanel Grid.Row="2" Grid.ColumnSpan="12" Orientation="Horizontal">
                                     <!--<TextBlock FontSize="14px" HorizontalAlignment="Left" Margin="5 5 0 0">
                                         <Run Text="AI版本:" Foreground="#666666"/> 
                                         <Run Text="{Binding AiVer}" Foreground="#000000" FontSize="14px"/>
                                     </TextBlock>-->
-                                    <TextBlock FontSize="14px" HorizontalAlignment="Left"  Margin="5 10 0 0"
+                                    <TextBlock FontSize="14px" HorizontalAlignment="Left"  Margin="5 10 5 0"
                                            Text="{Binding AiVer, StringFormat='AI版本: {0}'}" Foreground="#000000"/>
+
+                                    <TextBlock FontSize="14px" HorizontalAlignment="Left"  Margin="5 10 0 0"
+                                           Text="{Binding CompleteValue, StringFormat='完整值: {0}'}" Foreground="#000000"/>
                                 </StackPanel>
                                 
                             </Grid>
@@ -297,12 +304,15 @@
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
                             <StackPanel Orientation="Vertical" VerticalAlignment="Center">
+                                <Button x:Name="btnConfig" Width="Auto" FontSize="13px" Content="配置" Foreground="#FF4C4C" VerticalAlignment="Center"
+                                        Click="BtnConfig_Click"
+                                        BorderBrush="Transparent" BorderThickness="0 0 0 0" Background="Transparent" Margin="0 0 0 5" />
                                 <Button x:Name="btnLogViewer" Width="Auto" FontSize="13px" Content="日志" Foreground="#2196F3" VerticalAlignment="Center"
                                         Click="BtnLogViewer_Click"
-                                        BorderBrush="Transparent" BorderThickness="0 0 0 0" Background="Transparent" Margin="0 0 0 10" />
+                                        BorderBrush="Transparent" BorderThickness="0 0 0 0" Background="Transparent" Margin="0 0 0 5" />
                                 
                                 <Button x:Name="btnDelDetailItem" Width="Auto" FontSize="13px" Content="删除" Foreground="#2196F3" VerticalAlignment="Center"
-                                        BorderBrush="Transparent" BorderThickness="0 0 0 0" Background="Transparent" Margin="0 0 0 10"
+                                        BorderBrush="Transparent" BorderThickness="0 0 0 0" Background="Transparent" Margin="0 0 0 5"
                                         Click="BtnDelDetailItem_Click"  />
                                 
                                 <Button x:Name="btnRecongDetailItem" Width="Auto" FontSize="13px" Content="识别" Foreground="#2196F3" VerticalAlignment="Center"
@@ -325,23 +335,71 @@
                 </DataGridTemplateColumn>-->
 
 
-                <DataGridTemplateColumn Header="识别信息" Width="140">
+                <DataGridTemplateColumn Header="配置信息1" Width="110">
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
-                            <StackPanel Visibility="{Binding ResultVisiable}" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">
+                            <!--<StackPanel Visibility="{Binding ResultVisiable}" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">-->
+                            <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">
                                 <TextBlock FontSize="14px" Text="{Binding MeterTypeName}" 
                                            Foreground="#000000" Margin="0 0 0 5" HorizontalAlignment="Center"/>
                                 <TextBlock FontSize="14px" Text="{Binding NumCountName}" 
                                            Foreground="#000000" Margin="0 0 0 5" HorizontalAlignment="Center" />
+                                <TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 0"
+                                           Text="{Binding LastUnitName,Mode=OneWay}" Foreground="#000000"/>
+                                <!--<TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 5"
+                                           Text="{Binding BrightValName,Mode=OneWay}" Foreground="#000000"/>-->
+                            </StackPanel>
+                        </DataTemplate>
+                    </DataGridTemplateColumn.CellTemplate>
+                </DataGridTemplateColumn>
+
+                <DataGridTemplateColumn Header="配置信息2" Width="120">
+                    <DataGridTemplateColumn.CellTemplate>
+                        <DataTemplate>
+                            <!--<StackPanel Visibility="{Binding ResultVisiable}" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">-->
+                            <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">
                                 <TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 5"
+                                           Text="{Binding FlowRateName,Mode=OneWay}" Foreground="#000000"/>
+                                <TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 0"
                                            Text="{Binding LastUnitName,Mode=OneWay}" Foreground="#000000"/>
                             </StackPanel>
                         </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>
 
-                
-                
+                <DataGridTemplateColumn Header="初值" Width="100">
+                    <DataGridTemplateColumn.CellTemplate>
+                        <DataTemplate>
+                            <!--<StackPanel Visibility="{Binding ResultVisiable}" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">-->
+                            <!--<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">
+                                <TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 5"
+                                           Text="{Binding LastValueName,Mode=OneWay}" Foreground="#000000"/>
+                                <TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 5"
+                                           Text="{Binding LastTimeName1,Mode=OneWay}" Foreground="#000000"/>
+                                <TextBlock FontSize="14px" HorizontalAlignment="Center"  Margin="0 0 0 0"
+                                           Text="{Binding LastTimeName2,Mode=OneWay}" Foreground="#000000" />
+                            </StackPanel>-->
+                            <TextBlock FontSize="14px" HorizontalAlignment="Left" VerticalAlignment="Center"  Margin="10 0 0 0"
+                                           Text="{Binding LastValue,Mode=OneWay}" Foreground="#000000"/>
+                        </DataTemplate>
+                    </DataGridTemplateColumn.CellTemplate>
+                </DataGridTemplateColumn>
+
+
+                <DataGridTemplateColumn Header="区域信息" Width="250">
+                    <DataGridTemplateColumn.CellTemplate>
+                        <DataTemplate>
+                            <!--<StackPanel Visibility="{Binding ResultVisiable}" Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0,0,0">-->
+                            <StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5,0,0,0">
+                                <TextBlock FontSize="14px" Text="{Binding MeterRegionName}" TextWrapping="Wrap"
+                                           Foreground="#000000" Margin="0 0 0 10" HorizontalAlignment="Left"/>
+                                <TextBlock FontSize="14px" Text="{Binding FeatureRegionName}" TextWrapping="Wrap"
+                                           Foreground="#000000" Margin="0 0 0 5" HorizontalAlignment="Left" />
+                            </StackPanel>
+                        </DataTemplate>
+                    </DataGridTemplateColumn.CellTemplate>
+                </DataGridTemplateColumn>
+
 
             </DataGrid.Columns>
         </DataGrid>

+ 80 - 11
MeterVision/Single/UCSingleGrid.xaml.cs

@@ -10,6 +10,7 @@ using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.IO;
 using System.Net;
+using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 //using System.Linq;
 //using System.Text;
@@ -437,18 +438,27 @@ namespace MeterVision.Single
 
         public async void DragRecong(string srcImagePath)
         {
-            var singleDetail = new SingleDetailItem()
+            var singleDetailItem = new SingleDetailItem()
             {
                 SingleDetailId = Guid.NewGuid().ToString(),
                 CreateTime = ThisApp.GetNowTime_yyyyMMddHHmmss(),
-                SrcImage = srcImagePath
+                SrcImage = srcImagePath,
+                DeviceSn = "",
+                SampleTime = ""
             };
-            //DBSingle.InsertTSingleDetail0(singleDetail);
 
-            bool blRun = await RunAI(true,singleDetail);
-            Console.Write($"Run:{blRun}");
+            if(TryExtractNumbers(srcImagePath,out string deviceSn,out string sampleTime))
+            {
+                singleDetailItem.DeviceSn = deviceSn;
+                singleDetailItem.SampleTime = sampleTime;
+            }
+
+            TSingleDetail singleDetail = new TSingleDetail();
+            ObjectHelper.CopyMatchingFields(singleDetailItem, singleDetail);
 
-            if (blRun)
+            //await DBSingle.InsertTSingleDetail0(singleDetail);
+
+            if (DBSingle.InsertTSingleDetail0(singleDetail))
             {
                 SingleDetailPage.PageNumber = 1;
                 await LoadSingleDetailItemList();
@@ -457,13 +467,50 @@ namespace MeterVision.Single
                     SelectedSingleDetailItem = SingleDetailItemList[0];
                     dgSingle.ScrollIntoView(dgSingle.Items[0]);
                 }
-                //MessageBox.Show(Application.Current.MainWindow, "识别成功!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
-                //识别成功不提示
             }
-            else
+
+            //DBSingle.InsertTSingleDetail0(singleDetail);
+
+            //只插入数据,不做识别,因为当前版本的AI识别需要配置
+            //DBSingle.InsertSingleDetail()
+
+            //bool blRun = await RunAI(true,singleDetail);
+            //Console.Write($"Run:{blRun}");
+
+            //if (blRun)
+            //{
+            //    SingleDetailPage.PageNumber = 1;
+            //    await LoadSingleDetailItemList();
+            //    if (SingleDetailItemList.Count > 0)
+            //    {
+            //        SelectedSingleDetailItem = SingleDetailItemList[0];
+            //        dgSingle.ScrollIntoView(dgSingle.Items[0]);
+            //    }
+            //    //MessageBox.Show(Application.Current.MainWindow, "识别成功!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+            //    //识别成功不提示
+            //}
+            //else
+            //{
+            //    MessageBox.Show(Application.Current.MainWindow, "识别失败!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+            //}
+        }
+
+        static bool TryExtractNumbers(string fileName, out string deviceSn, out string recvTime)
+        {
+            deviceSn = string.Empty;
+            recvTime = string.Empty;
+
+            string pattern = @"^(\d{11})_(\d{14})_.*$";
+            Match match = Regex.Match(fileName, pattern);
+
+            if (match.Success)
             {
-                MessageBox.Show(Application.Current.MainWindow, "识别失败!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+                deviceSn = match.Groups[1].Value;
+                recvTime = match.Groups[2].Value;
+                return true;
             }
+
+            return false;
         }
 
         //处理拖拽的文件
@@ -791,7 +838,8 @@ namespace MeterVision.Single
                             //    MessageBox.Show(Application.Current.MainWindow, "start1");
                             //});
 
-                            resultModel = faRun.StartRecognition(singleDetailItem.SrcImage);
+                            //resultModel = faRun.StartRecognition(singleDetailItem.SrcImage);
+                            resultModel = faRun.StartRecognition(singleDetailItem);
 
                             //Application.Current.Dispatcher.Invoke(() =>
                             //{
@@ -842,6 +890,7 @@ namespace MeterVision.Single
                 {
                     //singleDetailItem = newSingleDetail;
                     ObjectHelper.CopyMatchingFields(newSingleDetail, singleDetailItem);
+                    //singleDetailItem.MeterType = 2;
                 }
                 return blRun;
             }
@@ -904,6 +953,26 @@ namespace MeterVision.Single
             }
         }
 
+        private void BtnConfig_Click(object sender, RoutedEventArgs e)
+        {
+            Button button = sender as Button;
+            if (button == null) return;
+
+            DataGridRow row = FindAncestor<DataGridRow>(button);
+            if (row == null) return;
+
+            SingleDetailItem detailItem = row.Item as SingleDetailItem;
+            EditSingleConfig dialog = new EditSingleConfig(detailItem)
+            {
+                Owner = Application.Current.MainWindow,
+                WindowStartupLocation = WindowStartupLocation.CenterOwner
+            };
+            if (dialog.ShowDialog() == true)
+            {
+                detailItem = dialog.mDetailItem;
+            }
+        }
+
         ///////////////////////////////////////////////////////////////////////////
     }
 

+ 1 - 1
MeterVision/Stand/UCStandMain.xaml

@@ -231,7 +231,7 @@
             <Grid Grid.Row="0" Background="WhiteSmoke">
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="*" />
-                    <ColumnDefinition Width="400"/>
+                    <ColumnDefinition Width="0"/>
                 </Grid.ColumnDefinitions>
                 
                 <!--右侧上左站点明细-->

+ 9 - 31
MeterVision/Station/UCStationGrid.xaml

@@ -167,13 +167,6 @@
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>
 
-                <!--<DataGridTemplateColumn Header="指针个数" Width="80">
-                    <DataGridTemplateColumn.CellTemplate>
-                        <DataTemplate>
-                            <TextBlock Text="{Binding IndCountName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
-                        </DataTemplate>
-                    </DataGridTemplateColumn.CellTemplate>
-                </DataGridTemplateColumn>-->
 
                 <DataGridTemplateColumn Header="尾数单位" Width="100">
                     <DataGridTemplateColumn.CellTemplate>
@@ -183,50 +176,35 @@
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>
 
-                <!--<DataGridTemplateColumn Header="指针尾数单位" Width="100">
-                    <DataGridTemplateColumn.CellTemplate>
-                        <DataTemplate>
-                            <TextBlock Text="{Binding LastIndUnitName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
-                        </DataTemplate>
-                    </DataGridTemplateColumn.CellTemplate>
-                </DataGridTemplateColumn>-->
-
-                <DataGridTemplateColumn Header="表盘区域" Width="120">
+                <DataGridTemplateColumn Header="表底读数" Width="100">
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
-                            <TextBlock Text="{Binding DialRegion}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            <TextBlock Text="{Binding LastValueName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                         </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>
-                
-                <DataGridTemplateColumn Header="数字区域 \ 指针坐标" Width="200">
+
+                <DataGridTemplateColumn Header="读数时间" Width="150">
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
-                            <TextBlock Text="{Binding FeatureRegionName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            <TextBlock Text="{Binding LastTimeName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                         </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>
 
-                <!--<DataGridTemplateColumn Header="首位指针坐标" Width="120">
-                    <DataGridTemplateColumn.CellTemplate>
-                        <DataTemplate>
-                            <TextBlock Text="{Binding HtRegionName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
-                        </DataTemplate>
-                    </DataGridTemplateColumn.CellTemplate>
-                </DataGridTemplateColumn>-->
 
-                <DataGridTemplateColumn Header="表底读数" Width="100">
+                <DataGridTemplateColumn Header="表盘区域" Width="120">
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
-                            <TextBlock Text="{Binding LastValueName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            <TextBlock Text="{Binding DialRegion}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                         </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>
 
-                <DataGridTemplateColumn Header="读数时间" Width="150">
+                <DataGridTemplateColumn Header="数字区域 \ 指针坐标" Width="220">
                     <DataGridTemplateColumn.CellTemplate>
                         <DataTemplate>
-                            <TextBlock Text="{Binding LastTimeName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
+                            <TextBlock Text="{Binding FeatureRegionName}" FontSize="13px" Foreground="#000000" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                         </DataTemplate>
                     </DataGridTemplateColumn.CellTemplate>
                 </DataGridTemplateColumn>

+ 15 - 4
MeterVision/db/DBSingle.cs

@@ -12,15 +12,17 @@ namespace MeterVision.db
         public static bool InsertTSingleDetail0(TSingleDetail singlDetail)
         {
             // 构建插入的 SQL 语句
-            string sql = "INSERT INTO t_single_detail (single_detail_id, create_time, src_image) " +
-                         "VALUES (@SingleDetailId, @CreateTime, @SrcImage)";
+            string sql = "INSERT INTO t_single_detail (single_detail_id, create_time, src_image, device_sn, sample_time) " +
+                         "VALUES (@SingleDetailId, @CreateTime, @SrcImage, @DeviceSn, @SampleTime)";
 
             // 创建参数数组
             SQLiteParameter[] parameters = new SQLiteParameter[]
             {
                 new SQLiteParameter("@SingleDetailId", singlDetail.SingleDetailId),
                 new SQLiteParameter("@CreateTime", singlDetail.CreateTime),
-                new SQLiteParameter("@SrcImage", singlDetail.SrcImage)
+                new SQLiteParameter("@SrcImage", singlDetail.SrcImage),
+                new SQLiteParameter("@DeviceSn", singlDetail.DeviceSn),
+                new SQLiteParameter("@SampleTime", singlDetail.SampleTime)
             };
 
             try
@@ -130,6 +132,8 @@ namespace MeterVision.db
                     result_type = @ResultType,
                     raw_value = @RawValue,
                     final_value = @FinalValue,
+                    complete_value = @CompleteValue,
+                    result_meter = @ResultMeter,
                     ai_ver = @AiVer,
                     debug_info = @DebugInfo,
                     log_path = @LogPath
@@ -146,6 +150,8 @@ namespace MeterVision.db
                 new SQLiteParameter("@ResultType", detail.ResultType),
                 new SQLiteParameter("@RawValue", detail.RawValue),
                 new SQLiteParameter("@FinalValue", detail.FinalValue),
+                new SQLiteParameter("@CompleteValue",detail.CompleteValue),
+                new SQLiteParameter("@ResultMeter", detail.ResultMeter),
                 new SQLiteParameter("@AiVer", detail.AiVer),
                 new SQLiteParameter("@DebugInfo", detail.DebugInfo),
                 new SQLiteParameter("@LogPath", detail.LogPath)
@@ -185,7 +191,8 @@ namespace MeterVision.db
             string sql = @"
                 SELECT single_detail_id, create_time, src_image, run_flag, run_time,
                        dst_image, meter_type, digit_count, pointer_count, last_unit,
-                       result_type, raw_value, final_value, ai_ver, debug_info, log_path, memo,
+                       result_type, raw_value, final_value, complete_value, result_meter,ai_ver, 
+                       debug_info, log_path, memo,device_sn, sample_time,
                        bright_val,flow_rate,meter_region,feature_region,last_value,last_time
                 FROM t_single_detail
                 ORDER BY create_time DESC
@@ -220,10 +227,14 @@ namespace MeterVision.db
                         ResultType = reader.GetInt32(reader.GetOrdinal("result_type")),
                         RawValue = reader.GetString(reader.GetOrdinal("raw_value")),
                         FinalValue = reader.GetString(reader.GetOrdinal("final_value")),
+                        CompleteValue = reader.GetString(reader.GetOrdinal("complete_value")),
+                        ResultMeter = reader.GetInt32(reader.GetOrdinal("result_meter")),
                         AiVer = reader.GetString(reader.GetOrdinal("ai_ver")),
                         DebugInfo = reader.GetString(reader.GetOrdinal("debug_info")),
                         LogPath = reader.GetString(reader.GetOrdinal("log_path")),
                         Memo = reader.GetString(reader.GetOrdinal("memo")),
+                        DeviceSn = reader.GetString(reader.GetOrdinal("device_sn")),
+                        SampleTime = reader.GetString(reader.GetOrdinal("sample_time")),
                         BrightVal = reader.GetDouble(reader.GetOrdinal("bright_val")),
                         FlowRate = reader.GetInt32(reader.GetOrdinal("flow_rate")),
                         MeterRegion = reader.GetString(reader.GetOrdinal("meter_region")),

+ 3 - 5
MeterVision/db/TPatchDetail.cs

@@ -70,9 +70,7 @@ namespace MeterVision.db
             RunFlag = 1;
             RunTime = ThisApp.GetNowTime_yyyyMMddHHmmss();
             DstImage = resultModel.DstImage;
-            MeterType = resultModel.MeterType;
-            DigitCount = resultModel.DigitCount;
-            PointerCount = resultModel.PointerCount;
+            //MeterType = resultModel.MeterType;            
             LastUnit = resultModel.SLastUnit;
             ResultType = resultModel.ResultType;
             RawValue = resultModel.SRawValue;
@@ -105,8 +103,8 @@ namespace MeterVision.db
             RunTime = ThisApp.GetNowTime_yyyyMMddHHmmss();
             DstImage = resultModel.DstImage;
             MeterType = resultModel.MeterType;
-            DigitCount = resultModel.DigitCount;
-            PointerCount = resultModel.PointerCount;
+            //DigitCount = resultModel.DigitCount;
+            //PointerCount = resultModel.PointerCount;
             LastUnit = resultModel.SLastUnit;
             ResultType = resultModel.ResultType;
             RawValue = resultModel.SRawValue;

+ 17 - 8
MeterVision/db/TSingleDetail.cs

@@ -1,4 +1,5 @@
 using MeterVision.model;
+using MeterVision.Util;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -24,11 +25,12 @@ namespace MeterVision.db
         public int ResultType { get; set; }         // RESULT_TYPE
         public string RawValue { get; set; }      // STAND_VALUE
         public string FinalValue { get; set; }     // RESULT_VALUE
+        public string CompleteValue { get; set; }
+        public int ResultMeter { get; set; }
         public string AiVer { get; set; }           // AI_VER
         public string DebugInfo { get; set; }       // DEBUG_INFO
         public string LogPath { get; set; }         // LOG_PATH
         public string Memo { get; set; }
-
         public double BrightVal { get; set; }
         public int FlowRate { get; set; }
         public string MeterRegion { get; set; }
@@ -36,6 +38,8 @@ namespace MeterVision.db
         public double LastValue { get; set; }
         public string LastTime { get; set; }
 
+        public string DeviceSn { get; set; }
+        public string SampleTime { get; set; }
 
         public TSingleDetail()
         {
@@ -61,27 +65,32 @@ namespace MeterVision.db
             FeatureRegion = string.Empty;
             LastValue = 0;
             LastTime = string.Empty;
+            ResultMeter = 0;
+            DeviceSn = "";
+            SampleTime = "";
+            CompleteValue = "";
         }
 
         public TSingleDetail(SingleDetailItem singleDetail,ResultModel resultModel)
         {
-            SingleDetailId = singleDetail.SingleDetailId;
-            CreateTime = singleDetail.CreateTime;
-            SrcImage = singleDetail.SrcImage;
+            //SingleDetailId = singleDetail.SingleDetailId;
+            //CreateTime = singleDetail.CreateTime;
+            //SrcImage = singleDetail.SrcImage;
+
+            ObjectHelper.CopyMatchingFields(singleDetail, this);
 
             RunFlag = 1;
             RunTime = ThisApp.GetNowTime_yyyyMMddHHmmss();
             DstImage = resultModel.DstImage;
-            //MeterType = resultModel.MeterType;
-            //DigitCount = resultModel.DigitCount;
-            //PointerCount = resultModel.PointerCount;
-            //LastUnit = resultModel.SLastUnit;
+            ResultMeter = resultModel.MeterType;
             ResultType = resultModel.ResultType;
             RawValue = resultModel.SRawValue;
             FinalValue = resultModel.sFinalValue;
+            CompleteValue = resultModel.sCompleteValue;
             AiVer = resultModel.AiVer;
             LogPath = resultModel.LogPath;
 
+
             // 使用 LINQ 进行转换和条件判断,并过滤掉空字符串
             //var formattedBytes = resultModel.DebugInfoBytes.Select(b => b == 88 ? "" : b.ToString())
             //                          .Where(s => !string.IsNullOrEmpty(s));

+ 12 - 2
MeterVision/model/ResultModel.cs

@@ -32,9 +32,9 @@ namespace MeterVision.model
         //仪表类型(0:不是水表图片,1:数字+指针,2:全指针 3:全数字)
         public int MeterType { get; set; }
 
-        public int DigitCount { get; set; }
+        //public int DigitCount { get; set; }
         
-        public int PointerCount { get; set; }
+        //public int PointerCount { get; set; }
 
         //小数尾数
         public int DecimalPlaces
@@ -73,6 +73,16 @@ namespace MeterVision.model
             get => GetSValueByIValue(FinalValue);
         }
 
+        public ulong CompleteValue { get; set; }
+
+        public string sCompleteValue
+        {
+            get
+            {
+                return ((double)CompleteValue / divisor).ToString();
+            }
+        }
+
         public string AiVer { get; set; }
 
         public byte[] DebugInfoBytes { get; set; }

+ 135 - 8
MeterVision/model/SingleDetailItem.cs

@@ -133,6 +133,8 @@ namespace MeterVision.model
                     _meterType = value;
                     OnPropertyChanged(nameof(MeterType));
                     OnPropertyChanged(nameof(MeterTypeName)); // 依赖 MeterType 更新 MeterTypeName
+                    OnPropertyChanged(nameof(FeatureRegionName));
+                    OnPropertyChanged(nameof(MeterRegionName));
                 }
             }
         }
@@ -245,14 +247,42 @@ namespace MeterVision.model
                 if (_finalValue != value)
                 {
                     _finalValue = value;
-                    if(MeterType == 0)
-                    {
-                        _finalValue = "";
-                    }
+                    //if(MeterType == 0)
+                    //{
+                    //    _finalValue = "";
+                    //}
                     OnPropertyChanged(nameof(FinalValue));
                 }
             }
         }
+        private string _completeValue;
+        public string CompleteValue
+        {
+            get => _completeValue;
+            set
+            {
+                if(_completeValue != value)
+                {
+                    _completeValue = value;
+                    OnPropertyChanged(nameof(CompleteValue));
+                }
+            }
+        }
+
+        //AI识别出的水表类型
+        private int _resultMeter;
+        public int ResultMeter
+        {
+            get => _resultMeter;
+            set
+            {
+                if (_resultMeter != value)
+                {
+                    _resultMeter = value;
+                    OnPropertyChanged(nameof(ResultMeter));
+                }
+            }
+        }
 
         private string _aiVer;
         public string AiVer
@@ -313,8 +343,8 @@ namespace MeterVision.model
             }
         }
 
-        private string _brightVal;
-        public string BrightVal
+        private double _brightVal;
+        public double BrightVal
         {
             get => _brightVal;
             set
@@ -323,10 +353,13 @@ namespace MeterVision.model
                 {
                     _brightVal = value;
                     OnPropertyChanged(nameof(BrightVal));
+                    OnPropertyChanged(nameof(BrightValName));
                 }
             }
         }
 
+        public string BrightValName => $"亮度: {BrightVal}";
+
         private int _flowRate;
         public int FlowRate
         {
@@ -337,10 +370,16 @@ namespace MeterVision.model
                 {
                     _flowRate = value;
                     OnPropertyChanged(nameof(FlowRate));
+                    OnPropertyChanged(nameof(FlowRateName));
                 }
             }
         }
 
+        public string FlowRateName
+        {
+            get => $"最大流量: {FlowRate}";
+        }
+
         private string _meterRegion;
         public string MeterRegion
         {
@@ -351,6 +390,22 @@ namespace MeterVision.model
                 {
                     _meterRegion = value;
                     OnPropertyChanged(nameof(MeterRegion));
+                    OnPropertyChanged(nameof(MeterRegionName));
+                }
+            }
+        }
+
+        public string MeterRegionName
+        {
+            get
+            {
+                if(MeterType == 1 || MeterType == 2 || MeterType == 3)
+                {
+                    return $"表盘: {MeterRegion}";
+                }
+                else
+                {
+                    return string.Empty;
                 }
             }
         }
@@ -365,10 +420,31 @@ namespace MeterVision.model
                 {
                     _featureRegion = value;
                     OnPropertyChanged(nameof(FeatureRegion));
+                    OnPropertyChanged(nameof(FeatureRegionName));
+                }
+            }
+        }
+
+        public string FeatureRegionName
+        {
+            get
+            {
+                if(MeterType == 1 || MeterType == 3)
+                {
+                    return $"数字区域: {FeatureRegion}";
+                }
+                else if(MeterType == 2)
+                {
+                    return $"首位指针: {FeatureRegion}";
+                }
+                else
+                {
+                    return string.Empty;
                 }
             }
         }
 
+
         private double _lastValue;
         public double LastValue
         {
@@ -379,10 +455,14 @@ namespace MeterVision.model
                 {
                     _lastValue = value;
                     OnPropertyChanged(nameof(LastValue));
+                    OnPropertyChanged(nameof(LastValueName));
                 }
             }
         }
 
+        public string LastValueName => $"初值: {LastValue}";
+
+
         private string _lastTime;
         public string LastTime
         {
@@ -393,6 +473,41 @@ namespace MeterVision.model
                 {
                     _lastTime = value;
                     OnPropertyChanged(nameof(LastTime));
+                    OnPropertyChanged(nameof(LastTimeName1));
+                    OnPropertyChanged(nameof(LastTimeName2));
+                }
+            }
+        }
+
+        public string LastTimeName1 => ThisApp.ConvertDateFormat_Date(LastTime);
+        public string LastTimeName2 => ThisApp.ConvertDateFormat_Time(LastTime);
+
+
+
+        private string _deviceSn;
+        public string DeviceSn
+        {
+            get => _deviceSn;
+            set
+            {
+                if(_deviceSn != value)
+                {
+                    _deviceSn = value;
+                    OnPropertyChanged(nameof(DeviceSn));
+                }
+            }
+        }
+
+        private string _sampleTime;
+        public string SampleTime
+        {
+            get => _sampleTime;
+            set
+            {
+                if(_sampleTime != value)
+                {
+                    _sampleTime = value;
+                    OnPropertyChanged(nameof(SampleTime));
                 }
             }
         }
@@ -422,8 +537,8 @@ namespace MeterVision.model
                 case 5:
                     return "压力表";
                 default:
-                    return "";
-                    //return "非水表";
+                    //return "";
+                    return "非水表";
             }
         }
 
@@ -468,6 +583,18 @@ namespace MeterVision.model
             return sInfos.Select(s => s == "88" ? "" : s).ToArray();
         }
 
+
+    //    complete_value = @CompleteValue,
+    //    result_meter = @ResultMeter,
+    //   new SQLiteParameter("@CompleteValue", detail.CompleteValue),
+    //   new SQLiteParameter("@ResultMeter", detail.ResultMeter),
+
+    //CompleteValue = reader.GetString(reader.GetOrdinal("complete_value")),
+    //ResultMeter = reader.GetInt32(reader.GetOrdinal("result_meter")),
+
+    //DeviceSn = reader.GetString(reader.GetOrdinal("device_sn")),
+    //SampleTime = reader.GetString(reader.GetOrdinal("sample_time")),
+
         //-------------------------------------------------
     }
 }