Compare commits

..

No commits in common. "main" and "98d34acbb1be76832c97a6889078b525cd230d10" have entirely different histories.

225 changed files with 47380 additions and 1 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

365
.gitignore vendored Normal file
View File

@ -0,0 +1,365 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# 忽略 common 文件夹及其所有内容
/common/

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
2个路由器串联下级路由器LAN口设置的方法连接第2个路由器网络进设置页面先修改IP例如上级路由的IP为192.168.0.1则将第2个路由修改为192.168.0.2然后在上网设置那WAN口数量设置成1.再点击路由设置--高级设置--修改路由器地址---DHCP服务器修改为OFF。 进行以上的操作之后,请将路由器电源拔掉再插一次,等待路由器重启完毕之后看是否能正常上网。
连接LAN口 关闭DHCP服务
192.168.50.99

View File

@ -0,0 +1,40 @@
<Window x:Class="Common.AlarmPopUpWindow"
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:Common"
mc:Ignorable="d" ResizeMode="NoResize" Topmost="True" WindowStartupLocation="CenterScreen"
Title="系统报警" Height="450" Width="800">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题区域 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="PLC报警信息" FontSize="16" FontWeight="Bold" Foreground="Red"/>
</StackPanel>
<!-- 报警信息列表 -->
<ScrollViewer Grid.Row="1" Margin="20,5,10,0">
<ItemsControl x:Name="AlarmItems">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text="{Binding Value}" FontWeight="Bold" Margin="10,5,0,0"/>
<Separator Margin="0,5"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!-- 按钮区域 -->
<Button Grid.Row="2" Content="确认" Width="100" Height="30"
HorizontalAlignment="Center" Click="OkButton_Click" Style="{StaticResource okBtnStyle}"/>
</Grid>
</Window>

View File

@ -0,0 +1,33 @@
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 Common
{
/// <summary>
/// AlarmPopUpWindow.xaml 的交互逻辑
/// </summary>
public partial class AlarmPopUpWindow : Window
{
public AlarmPopUpWindow(Dictionary<string,string> alarmInfo)
{
InitializeComponent();
AlarmItems.ItemsSource = alarmInfo;
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

View File

@ -0,0 +1,155 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace Common.Models
{
/// <summary>
/// 工装位置区域划分
/// </summary>
public enum BoxAreaEnum
{
_手套箱传送平台 = 1,
_Tip托盘存放平台 = 2,
_开关盖区域 = 3,
_称重模块 = 4,
_扫码区域 = 6,
_加样头存放平台 = 10,
_托盘存放平台 = 12,
_自动加样模块内 = 11,
}
/// <summary>
/// 工装类型
/// </summary>
public enum BoxTypeEnum
{
_40mL原液工装 = 1,
_12mL样品工装 = 2,
_5mL样品工装 = 3,
//_2mL外瓶工装 = 4,
//_滤芯瓶工装 = 5,
_16mL粉末瓶工装=6,
_125mL粉末瓶工装,
_50uLTip头工装,
_300uLTip头工装,
_1000uLTip头工装,
}
[Serializable]
[XmlInclude(typeof(SourcePowderBottleBoxModel))]
[XmlInclude(typeof(SourcePowderBottleBoxModel_125ml))]
[XmlInclude(typeof(SampleBottleBoxModel))]
[XmlInclude(typeof(SourceLiquidBottleBoxModel))]
[XmlInclude(typeof(TipBoxModel))]
public class BaseBottleBox : ObservableObject
{
private BoxAreaEnum boxarea = BoxAreaEnum._手套箱传送平台;
/// <summary>
/// 工装所在的工装区域
/// </summary>
public BoxAreaEnum BoxArea
{
get { return boxarea; }
set { SetProperty(ref boxarea, value); }
}
private BoxTypeEnum fixturetype = BoxTypeEnum._40mL原液工装;
/// <summary>
/// 工装类型
/// </summary>
[JsonProperty("fixtureType")]
public BoxTypeEnum FixtureType
{
get => fixturetype;
set => SetProperty(ref fixturetype, value);
}
private string _boxsn = string.Empty;
/// <summary>
/// 载具的二维码信息
/// </summary>
[JsonProperty("snCode")]
public string BoxSNCode
{
get => _boxsn;
set
{
SetProperty(ref _boxsn, value);
}
}
private bool _isEmpty = false;
/// <summary>
/// 是否是空位置。true:表示空,可以放置工装 false:表示位置有工装
/// </summary>
public bool IsEmpty
{
get => _isEmpty;
set => SetProperty(ref _isEmpty, value);
}
private bool _isDoseFinish = false;
/// <summary>
/// 是否配置完成,针对样品工装来说
/// </summary>
public bool IsDoseFinish
{
get => _isDoseFinish;
set => SetProperty(ref _isDoseFinish, value);
}
private bool _isuseFinish = false;
/// <summary>
/// 是否用完成 针对tip头工装或者原液瓶工装
/// </summary>
public bool IsUseFinsh
{
get => _isuseFinish;
set => SetProperty(ref _isuseFinish, value);
}
private int boxid_indoseupload = 0;
/// <summary>
/// 工装在投料站 上料时位置(1-7)
/// </summary>
[JsonProperty("boxIdInDoseUpload")]
public int BoxId_inDoseUpload
{
get => boxid_indoseupload;
set => SetProperty(ref boxid_indoseupload, value);
}
private int _boxid_inDoseDnload = 0;
/// <summary>
/// 工装在投料站 下料时的位置(1-7)
/// </summary>
public int BoxId_inDoseDnload
{
get => _boxid_inDoseDnload;
set
{
SetProperty(ref _boxid_inDoseDnload, value);
}
}
private int _boxid_inDosingStation = 0;
/// <summary>
/// 工装在投料站中位置(1-8)
/// </summary>
public int BoxId_inDosingStation
{
get => _boxid_inDosingStation;
set
{
SetProperty(ref _boxid_inDosingStation, value);
}
}
}
}

View File

@ -0,0 +1,118 @@
using Common.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace Common.Models
{
/// <summary>
/// 样品瓶工装的模型
/// </summary>
[Serializable]
public class SampleBottleBoxModel : BaseBottleBox
{
private SampleBottleTypeEnum sampleBottleType = SampleBottleTypeEnum._5mL;
/// <summary>
/// 样品瓶类型 5ML/12ML
/// </summary>
public SampleBottleTypeEnum SampleBottleType
{
get { return sampleBottleType; }
set { SetProperty(ref sampleBottleType, value); }
}
private ObservableCollection<SampleBottleModel> sampleBottleList = new ObservableCollection<SampleBottleModel>();
/// <summary>
/// 一个样品工装中样品瓶的集合(12个样品瓶)
/// </summary>
public ObservableCollection<SampleBottleModel> SampleBottleList
{
get { return sampleBottleList; }
set { SetProperty(ref sampleBottleList, value); }
}
#region
//private bool haveBox = false;
///// <summary>
///// 投料站中放样品工装1-4个位置中 是否有样品瓶工装
///// </summary>
//public bool HaveBox
//{
// get => haveBox;
// set
// {
// SetProperty(ref haveBox, value);
// }
//}
#endregion
#region
private int _boxid_inReactUpload = 0;
/// <summary>
/// 样品工装在反应站中的上料时位置(1-6)
/// </summary>
public int BoxId_inReactUpload
{
get => _boxid_inReactUpload;
set
{
SetProperty(ref _boxid_inReactUpload, value);
}
}
private int _boxid_inhightempHC = 0;
/// <summary>
/// 样品工装在反应站中高温缓存位置(1-3),在高温模块上的缓存位置
/// </summary>
public int BoxId_inhightempHC
{
get => _boxid_inhightempHC;
set
{
SetProperty(ref _boxid_inhightempHC, value);
}
}
private int _boxid_inReactRack_layer = 3;
/// <summary>
/// 样品工装在反应站中料架中的第几层 默认三层或者四层
/// </summary>
public int BoxId_inReactRack_Layer
{
get => _boxid_inReactRack_layer;
set
{
SetProperty(ref _boxid_inReactRack_layer, value);
}
}
private int _boxid_inReactRack_pos = 0;
/// <summary>
/// 样品工装在反应站中料架中的第几层第几个位置1-10
/// </summary>
public int BoxId_inReactRack_Pos
{
get => _boxid_inReactRack_pos;
set
{
SetProperty(ref _boxid_inReactRack_pos, value);
}
}
public SampleBottleBoxModel()
{
}
#endregion
}
}

View File

@ -0,0 +1,519 @@
using Common.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml.Serialization;
namespace Common.Models
{
/// <summary>
/// 样品瓶状态
/// </summary>
public enum SampleBottleStateEnum
{
/// <summary>
/// 0:还未投料
/// </summary>
Empty = 0,
/// <summary>
/// 正在投料中
/// </summary>
IsDosing = 1,
DoseFinish = 2,
/// <summary>
/// 1高温反应中
/// </summary>
HighTemperaturReact = 3,
/// <summary>
/// 2低温反应中
/// </summary>
LowTemperaturReact = 4,
/// <summary>
///穿刺取样中
/// </summary>
PunctureSampling = 5,
/// <summary>
/// 开盖取样中
/// </summary>
OpenLidSampling = 6,
/// <summary>
/// 穿刺补液
/// </summary>
PunctureAddLiquid = 7,
/// <summary>
/// 开盖补液
/// </summary>
OpenLidAddLiquid = 8,
/// <summary>
/// 稀释中
/// </summary>
Dilute = 9,
/// <summary>
/// 过滤中
/// </summary>
Filter = 10,
}
/// <summary>
/// 反应温度选择
/// </summary>
public enum SampleBottleReactTemperatureEnum
{
,
,
,
}
/// <summary>
/// 反应模块的操作类型
/// </summary>
public enum ReactOperationEnum
{
,
,
,
}
/// <summary>
/// 取样方式
/// </summary>
public enum SampleMethod
{
穿,
,
}
/// <summary>
/// 补液方式
/// </summary>
public enum AddLiquidMethod
{
穿,
,
}
/// <summary>
/// 稀释方式
/// </summary>
public enum DiluteMethod
{
,
穿,
,
}
/// <summary>
/// 样品瓶盖子的状态
/// </summary>
public enum SampleBottleLidStateEnum
{
Close,
Open,
}
/// <summary>
/// 样品瓶类型
/// </summary>
public enum SampleBottleTypeEnum
{
_5mL = 1,
_12mL = 2,
}
[Serializable]
/// <summary>
/// 样品瓶属性
/// </summary>
public class SampleBottleModel : ObservableObject
{
private long sampleSeqId;
public long SampleSeqId
{
get { return sampleSeqId; }
set { SetProperty(ref sampleSeqId, value); }
}
private SampleBottleTypeEnum sampleBottleType = SampleBottleTypeEnum._5mL;
/// <summary>
/// 样品瓶类型 5ML/12ML
/// </summary>
public SampleBottleTypeEnum SampleBottleType
{
get { return sampleBottleType; }
set { SetProperty(ref sampleBottleType, value); }
}
private string _boxsn = string.Empty;
/// <summary>
/// 样品瓶所在的工装的二维码信息
/// </summary>
public string BoxSNCode
{
get => _boxsn;
set { SetProperty(ref _boxsn, value); }
}
private string _sn = string.Empty;
/// <summary>
/// 样品瓶的二维码信息
/// </summary>
public string SNCode
{
get => _sn;
set { SetProperty(ref _sn, value); }
}
private bool _haveBottle = false;
/// <summary>
/// 是否有样品瓶子
/// </summary>
public bool HaveBottle
{
get => _haveBottle;
set { SetProperty(ref _haveBottle, value); }
}
private bool bdosefinish = false;
/// <summary>
/// 是否配置完成
/// </summary>
public bool bDoseFinish
{
get { return bdosefinish; }
set { bdosefinish = value; }
}
private int _posId_inbox = 1;
/// <summary>
/// 样品瓶在工装中的第几个位置(1-12)
/// </summary>
public int PosId_InBox
{
get => _posId_inbox;
set { SetProperty(ref _posId_inbox, value); }
}
#region
private string _sampleContentName = string.Empty;
/// <summary>
/// 样品瓶投料结果内容名称
/// </summary>
public string SampleContentName
{
get { return _sampleContentName; }
set { SetProperty(ref _sampleContentName, value); }
}
private SampleBottleStateEnum _sampleBottleState = SampleBottleStateEnum.Empty;
/// <summary>
/// 样品瓶状态 正在哪一步操作中
/// </summary>
public SampleBottleStateEnum SampleBoxState
{
get => _sampleBottleState;
set
{
if (_sampleBottleState != value)
{
_sampleBottleState = value;
OnPropertyChanged(nameof(SampleBoxState));
OnPropertyChanged(nameof(DisplayColor_Dose));
OnPropertyChanged(nameof(DisplayColor_React));
}
}
}
/// <summary>
/// 样品瓶状态,在投料站中展示颜色
/// </summary>
public Brush DisplayColor_Dose
{
get
{
switch (SampleBoxState)
{
case SampleBottleStateEnum.Empty:
if (HaveBottle) return (Brush)new BrushConverter().ConvertFrom("#aff0b3");
else return (Brush)new BrushConverter().ConvertFrom("#dcd8d8"); //return "#d1caca".FromHex(); // 未配置:白色
case SampleBottleStateEnum.IsDosing:
return (Brush)new BrushConverter().ConvertFrom("#f6ee73"); // 状态:黄色
case SampleBottleStateEnum.HighTemperaturReact:
case SampleBottleStateEnum.LowTemperaturReact:
return (Brush)new BrushConverter().ConvertFrom("#e3612e"); // 状态:橘色
case SampleBottleStateEnum.OpenLidSampling:
case SampleBottleStateEnum.PunctureSampling:
return (Brush)new BrushConverter().ConvertFrom("#5f84ee"); // 紫色
case SampleBottleStateEnum.Dilute:
case SampleBottleStateEnum.Filter:
return (Brush)new BrushConverter().ConvertFrom("#61b2f5"); // 蓝色
default:
if (HaveBottle) return (Brush)new BrushConverter().ConvertFrom("#aff0b3");
else return (Brush)new BrushConverter().ConvertFrom("#dcd8d8");
}
}
}
/// <summary>
/// 样品瓶状态,在反应站展示颜色
/// </summary>
public Brush DisplayColor_React
{
get
{
switch (SampleBoxState)
{
case SampleBottleStateEnum.Empty:
if (HaveBottle) return (Brush)new BrushConverter().ConvertFrom("#aff0b3");
else return (Brush)new BrushConverter().ConvertFrom("#dcd8d8"); //return "#d1caca".FromHex(); // 未配置:白色
//case SampleBottleStateEnum.IsDosing:
// return (Brush)new BrushConverter().ConvertFrom("#f6ee73"); // 状态:黄色
case SampleBottleStateEnum.HighTemperaturReact:
case SampleBottleStateEnum.LowTemperaturReact:
return (Brush)new BrushConverter().ConvertFrom("#e3612e"); // 状态:橘色
case SampleBottleStateEnum.OpenLidSampling:
case SampleBottleStateEnum.PunctureSampling:
return (Brush)new BrushConverter().ConvertFrom("#5f84ee"); // 紫色
case SampleBottleStateEnum.Dilute:
case SampleBottleStateEnum.Filter:
return (Brush)new BrushConverter().ConvertFrom("#61b2f5"); // 蓝色
default:
if (HaveBottle) return (Brush)new BrushConverter().ConvertFrom("#aff0b3");
else return (Brush)new BrushConverter().ConvertFrom("#dcd8d8");
}
}
}
private SampleBottleLidStateEnum _sampleBottleLidState = SampleBottleLidStateEnum.Close;
/// <summary>
/// 样品瓶盖子的状态
/// </summary>
public SampleBottleLidStateEnum SampleBottleLidState
{
get => _sampleBottleLidState;
set => _sampleBottleLidState = value;
}
private ObservableCollection<MaterialDoseFunctionFlow> _materialDoseFlowList = new ObservableCollection<MaterialDoseFunctionFlow>();
/// <summary>
/// 样品瓶中投料站流程集合用于显示datagridView控件中
/// </summary>
public ObservableCollection<MaterialDoseFunctionFlow> MaterialDoseFlowList
{
get
{
return _materialDoseFlowList;
}
set
{
// 设置新值
if (value != null && SetProperty(ref _materialDoseFlowList, value))
{
// 先取消旧集合的事件订阅
_materialDoseFlowList.CollectionChanged -= LiquidFlowList_CollectionChanged;
// 订阅新集合的事件
_materialDoseFlowList.CollectionChanged += LiquidFlowList_CollectionChanged;
// 手动触发一次计算
LiquidFlowList_CollectionChanged(this, null);
}
}
}
#region
private double _estimatedDuration = 0;
/// <summary>
/// 预估投料耗时时间单位min
/// </summary>
public double EstimatedDuration
{
get => _estimatedDuration;
set
{
SetProperty(ref _estimatedDuration, value, nameof(EstimatedDuration));
{
if (remainDuration == 0) // 假设初始值为0代表未初始化
{
remainDuration = value;
OnPropertyChanged(nameof(RemainDuration));
}
OnPropertyChanged(nameof(ProgressPercentage));
}
}
}
// 液体流程集合项变化时更新时间
private void LiquidFlowList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
EstimatedDuration = 1 * _materialDoseFlowList.Count;
}
private double remainDuration = 0;
/// <summary>
/// 预估原样瓶配置剩余时间单位min
/// </summary>
public double RemainDuration
{
get { return remainDuration; }
set
{
SetProperty(ref remainDuration, value);
OnPropertyChanged(nameof(ProgressPercentage));
}
}
/// <summary>
/// 用于进度绑定的属性
/// </summary>
public double ProgressPercentage
{
get
{
if (EstimatedDuration <= 0) return 1.0;
double usedRatio = 1.0 - (RemainDuration / (EstimatedDuration)); // 注意单位转换
return Math.Max(0, Math.Min(1, usedRatio));
}
}
#endregion
public SampleBottleModel()
{
// ObservableCollection 会在项添加 / 删除时触发 CollectionChanged 事件,需在 ReactBottleModel 中订阅该事件,确保集合项变化时重新计算时间。
// 初始化集合时订阅 CollectionChanged 事件
_materialDoseFlowList.CollectionChanged += LiquidFlowList_CollectionChanged;
}
#endregion
#region
private int _highTempModuleNo = 0;
/// <summary>
/// 样品瓶在高温模块的编号1-4
/// </summary>
public int HighTempModuleNo
{
get => _highTempModuleNo;
set => _highTempModuleNo = value;
}
private int _pos_inhightemperatur = 0;
/// <summary>
/// 样品瓶在高温模块中的位置1-24
/// </summary>
public int Pos_InHighTemperatur
{
get => _pos_inhightemperatur;
set => _pos_inhightemperatur = value;
}
private int _lowTempModuleNo = 0;
/// <summary>
/// 样品瓶在低温模块的编号1-2
/// </summary>
public int LowTempModuleNo
{
get => _lowTempModuleNo;
set => _lowTempModuleNo = value;
}
private int _pos_inlowtemperatur = 0;
/// <summary>
/// 样品瓶在低温模块中的位置1-24
/// </summary>
public int Pos_InLowTemperatur
{
get => _pos_inlowtemperatur;
set => _pos_inlowtemperatur = value;
}
private SampleBottleReactTemperatureEnum reactTemperatureType = SampleBottleReactTemperatureEnum.;
/// <summary>
/// 反应温度方式
/// </summary>
public SampleBottleReactTemperatureEnum ReactTemperatureType
{
get { return reactTemperatureType; }
set
{
SetProperty(ref reactTemperatureType, value);
OnPropertyChanged(nameof(CurrentTemperatureModel));
}
}
private SampleBottleTemperatureModel tempmodel = new SampleBottleTemperatureModel();
public SampleBottleTemperatureModel CurrentTemperatureModel
{
get => tempmodel;
set => SetProperty(ref tempmodel, value);
}
private SampleMethod samplemethod = SampleMethod.穿;
/// <summary>
/// 取样方式
/// </summary>
public SampleMethod SampleMethod
{
get { return samplemethod; }
set
{
SetProperty(ref samplemethod, value);
}
}
private AddLiquidMethod addliquidmethod = AddLiquidMethod.穿;
/// <summary>
/// 补液方式
/// </summary>
public AddLiquidMethod AddLiquidType
{
get { return addliquidmethod; }
set
{
SetProperty(ref addliquidmethod, value);
}
}
private double height = 0;
/// <summary>
/// 穿刺取样高度
/// </summary>
public double PunchHeight
{
get { return height; }
set { SetProperty(ref height, value); }
}
private ObservableCollection<ReactSampleFunctionFlow> sampleFunctionFlowList = new ObservableCollection<ReactSampleFunctionFlow>();
/// <summary>
/// 取样流程
/// </summary>
public ObservableCollection<ReactSampleFunctionFlow> SampleFunctionFlowList
{
get { return sampleFunctionFlowList; }
set { SetProperty(ref sampleFunctionFlowList, value); }
}
private ObservableCollection<AddLiquidFunctionFlow> _addLiquidFlowList = new ObservableCollection<AddLiquidFunctionFlow>();
/// <summary>
/// 加液流程集合用于显示datagridView控件中
/// </summary>
public ObservableCollection<AddLiquidFunctionFlow> AddLiquidFunctionFlowList
{
get { return _addLiquidFlowList; }
set { SetProperty(ref _addLiquidFlowList, value); }
}
#endregion
}
}

View File

@ -0,0 +1,39 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Models
{
[Serializable]
public class SourceLiquidBottleBoxModel : BaseBottleBox
{
//private bool havebox = false;
///// <summary>
///// 是否有原液瓶工装
///// </summary>
//public bool HaveBox
//{
// get { return havebox; }
// set { SetProperty(ref havebox, value); }
//}
private ObservableCollection<SourceLiquidBottleModel> sourceliquidbottlecollection;
/// <summary>
/// 一个原液瓶工装中的原液瓶集合8个原液瓶
/// </summary>
public ObservableCollection<SourceLiquidBottleModel> SourceLiquidBottleCollection
{
get { return sourceliquidbottlecollection; }
set { SetProperty(ref sourceliquidbottlecollection, value); }
}
public SourceLiquidBottleBoxModel()
{
}
}
}

View File

@ -0,0 +1,200 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
namespace Common.Models
{
/// <summary>
/// 原液瓶的状态
/// </summary>
public enum SourceBottleStateEnum
{
/// <summary>
/// 空闲,没有在使用
/// </summary>
Idl = 0,
/// <summary>
/// 使用中
/// </summary>
Using,
}
/// <summary>
/// 原液瓶盖子的状态
/// </summary>
public enum SourceBottleLidStateEnum
{
Close,
Open,
}
[Serializable]
public class SourceLiquidBottleModel : ObservableObject
{
private bool _haveBottle = false;
/// <summary>
/// 是否有液体瓶子
/// </summary>
public bool HaveBottle
{
get => _haveBottle;
set
{
SetProperty(ref _haveBottle, value);
}
}
private int _posId = 1;
/// <summary>
/// 液体原液瓶在工装中的位置(1-8)
/// </summary>
public int PosId_InBox
{
get => _posId;
set
{
SetProperty(ref _posId, value);
}
}
private string _boxsn = string.Empty;
/// <summary>
/// 液体原液瓶所在载具的二维码信息
/// </summary>
public string BoxSNCode
{
get => _boxsn;
set
{
SetProperty(ref _boxsn, value);
}
}
private string _sn = string.Empty;
/// <summary>
/// 液体原液瓶的二维码信息
/// </summary>
public string SNCode
{
get => _sn;
set
{
SetProperty(ref _sn, value);
}
}
private string _sourceLiquidName = string.Empty;
/// <summary>
/// 液体原液瓶中液体名称
/// </summary>
public string SourceLiquidName
{
get => _sourceLiquidName;
set
{
if (_sourceLiquidName != value)
{
_sourceLiquidName = value;
OnPropertyChanged(nameof(SourceLiquidName));
OnPropertyChanged(nameof(LiquidDisplayColor));
}
}
}
public Brush LiquidDisplayColor
{
get
{
if (string.IsNullOrEmpty(SourceLiquidName)) //没有有名称
{
return (Brush)new BrushConverter().ConvertFrom("#dcd8d8");
}
else
{
return (Brush)new BrushConverter().ConvertFrom("#cab2ec");
}
}
}
private double _originVolume = 0;
/// <summary>
/// 初始体积
/// </summary>
public double OriginVolume
{
get => _originVolume;
set
{
if (_originVolume != value)
{
_originVolume = value;
_remainVolume = value;
OnPropertyChanged(nameof(OriginVolume));
OnPropertyChanged(nameof(RemainVolume));
}
}
}
private double _remainVolume = 0;
/// <summary>
/// 剩余体积 单位ML
/// </summary>
public double RemainVolume
{
get => _remainVolume;
set
{
SetProperty(ref _remainVolume, value);
}
}
//private double _density = 1.00;
///// <summary>
///// 密度,单位g/mL
///// </summary>
//public double Density
//{
// get => _density;
// set
// {
// SetProperty(ref _density, value);
// }
//}
private SourceBottleStateEnum _sourceBottleState = SourceBottleStateEnum.Idl;
/// <summary>
/// 液体原液瓶状态
/// </summary>
public SourceBottleStateEnum SourceLiquidBottleState
{
get => _sourceBottleState;
set
{
SetProperty(ref _sourceBottleState, value);
}
}
private SourceBottleLidStateEnum _LiquidBottleLidState = SourceBottleLidStateEnum.Close;
/// <summary>
/// 液体原液瓶盖子的状态
/// </summary>
public SourceBottleLidStateEnum LiquidBottleLidState
{
get => _LiquidBottleLidState;
set
{
SetProperty(ref _LiquidBottleLidState, value);
}
}
public SourceLiquidBottleModel()
{
}
}
}

View File

@ -0,0 +1,108 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Models
{
[Serializable]
public class SourcePowderBottleBoxModel : BaseBottleBox
{
private PowderBottleTypeEnum powderbottletype = PowderBottleTypeEnum._16mL;
/// <summary>
/// 粉末瓶类型
/// </summary>
public PowderBottleTypeEnum PowderBottleType
{
get { return powderbottletype; }
set { SetProperty(ref powderbottletype, value); }
}
//private bool havebox;
///// <summary>
///// 是否有 粉末瓶工装
///// </summary>
//public bool HaveBox
//{
// get { return havebox; }
// set { SetProperty(ref havebox, value); }
//}
//private int boxId;
///// <summary>
///// 粉末瓶工装的位置号1-6
///// </summary>
//public int BoxId
//{
// get { return boxId; }
// set { SetProperty(ref boxId, value); }
//}
private ObservableCollection<SourcePowderBottleModel> sourcepowderbottlecollection = new ObservableCollection<SourcePowderBottleModel>();
/// <summary>
/// 一个粉末瓶工装中的粉末瓶集合4个粉末瓶
/// </summary>
public ObservableCollection<SourcePowderBottleModel> SourcePowderBottleCollection
{
get { return sourcepowderbottlecollection; }
set { SetProperty(ref sourcepowderbottlecollection, value); }
}
public SourcePowderBottleBoxModel()
{
}
}
[Serializable]
public class SourcePowderBottleBoxModel_125ml : BaseBottleBox
{
private PowderBottleTypeEnum powderbottletype = PowderBottleTypeEnum._125mL;
/// <summary>
/// 粉末瓶类型
/// </summary>
public PowderBottleTypeEnum PowderBottleType
{
get { return powderbottletype; }
set { SetProperty(ref powderbottletype, value); }
}
//private bool havebox;
///// <summary>
///// 是否有 粉末瓶工装
///// </summary>
//public bool HaveBox
//{
// get { return havebox; }
// set { SetProperty(ref havebox, value); }
//}
//private int boxId;
///// <summary>
///// 粉末瓶工装的位置号1-6
///// </summary>
//public int BoxId
//{
// get { return boxId; }
// set { SetProperty(ref boxId, value); }
//}
private ObservableCollection<SourcePowderBottleModel> sourcepowderbottlecollection = new ObservableCollection<SourcePowderBottleModel>();
/// <summary>
/// 一个粉末瓶工装中的粉末瓶集合4个粉末瓶
/// </summary>
public ObservableCollection<SourcePowderBottleModel> SourcePowderBottleCollection
{
get { return sourcepowderbottlecollection; }
set { SetProperty(ref sourcepowderbottlecollection, value); }
}
public SourcePowderBottleBoxModel_125ml()
{
}
}
}

View File

@ -0,0 +1,193 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Common.Models
{
/// <summary>
/// 粉末瓶类型
/// </summary>
public enum PowderBottleTypeEnum
{
/// <summary>
/// 如果是16ml 一个工装放4个
/// </summary>
_16mL,
/// <summary>
/// 如果是125ml, 一个工装放2个
/// </summary>
_125mL,
}
[Serializable]
public class SourcePowderBottleModel : ObservableObject
{
private PowderBottleTypeEnum powderbottletype = PowderBottleTypeEnum._125mL;
public PowderBottleTypeEnum PowderBottleType
{
get { return powderbottletype; }
set { SetProperty(ref powderbottletype, value); }
}
private bool _haveBottle = false;
/// <summary>
/// 是否有粉末瓶子
/// </summary>
public bool HaveBottle
{
get => _haveBottle;
set
{
SetProperty(ref _haveBottle, value);
}
}
private int _sourceInde = 0;
/// <summary>
/// 粉末原液瓶在工装中的原本位置
/// </summary>
public int SourceIndex
{
get => _sourceInde;
set
{
SetProperty(ref _sourceInde, value);
}
}
private int _pos = 1;
/// <summary>
/// 粉末原液瓶在工装中的位置(1-4 对于125ml的粉末位置对应1-2)
/// </summary>
public int PosId_InBox
{
get => _pos;
set
{
SetProperty(ref _pos, value);
}
}
private string _sn = string.Empty;
/// <summary>
/// 粉末瓶的二维码信息
/// </summary>
public string SNCode
{
get => _sn;
set
{
SetProperty(ref _sn, value);
}
}
private string _boxsn = string.Empty;
/// <summary>
/// 粉末瓶所在载具的二维码信息
/// </summary>
public string BoxSNCode
{
get => _boxsn;
set
{
SetProperty(ref _boxsn, value);
}
}
private string _sourcePowderName = string.Empty;
/// <summary>
/// 粉末原液瓶中粉末名称
/// </summary>
public string SourcePowderName
{
get => _sourcePowderName;
set
{
if (_sourcePowderName != value)
{
_sourcePowderName = value;
OnPropertyChanged(nameof(SourcePowderName));
OnPropertyChanged(nameof(PowderDisplayColor));
}
}
}
public Brush PowderDisplayColor
{
get
{
if (string.IsNullOrEmpty(SourcePowderName)) //没有有名称
{
return (Brush)new BrushConverter().ConvertFrom("#dcd8d8");
}
else
{
return (Brush)new BrushConverter().ConvertFrom("#b9cbf5"); //#FFFAFAF7
}
}
}
private double _originWeight = 0.00;
/// <summary>
/// 粉末原液瓶初始重量
/// </summary>
public double OriginWeight
{
get => _originWeight;
set
{
if (_originWeight != value)
{
_originWeight = value;
_remainWeight = value;
OnPropertyChanged(nameof(OriginWeight));
OnPropertyChanged(nameof(RemainWeight));
}
}
}
private double _remainWeight = 0;
/// <summary>
/// 剩余重量 单位g
/// </summary>
public double RemainWeight
{
get => _remainWeight;
set
{
SetProperty(ref _remainWeight, value);
}
}
private SourceBottleStateEnum _state = SourceBottleStateEnum.Idl;
/// <summary>
/// 粉末原液瓶状态
/// </summary>
public SourceBottleStateEnum SourcePowderBottleState
{
get => _state;
set
{
SetProperty(ref _state, value);
}
}
private int _pos_inDosePowderHC = 1;
/// <summary>
/// 粉末原液瓶在投料工站中的10个粉末瓶缓存的位置(1-10)
/// </summary>
public int Pos_InDosePowderHC
{
get => _pos_inDosePowderHC;
set
{
SetProperty(ref _pos_inDosePowderHC, value);
}
}
}
}

View File

@ -0,0 +1,148 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Common.Models
{
public enum TipTypeEnum
{
_50UL = 1,
_300UL = 2,
_1000UL = 3,
}
[Serializable]
public class TipHeadItem : ObservableObject
{
private bool _isAvailable;
public bool IsAvailable
{
get => _isAvailable;
set
{
SetProperty(ref _isAvailable, value);
}
}
private int useindex = 0;
public int UseIndex
{
get { return useindex; }
set { SetProperty(ref useindex, value); }
}
public TipHeadItem()
{
}
}
[Serializable]
public class TipBoxModel : BaseBottleBox
{
private TipTypeEnum tipType;
/// <summary>
/// Tip头类型
/// </summary>
public TipTypeEnum TipType
{
get { return tipType; }
set { SetProperty(ref tipType, value); }
}
private bool _havebox = false;
/// <summary>
/// 是否有工装
/// </summary>
public bool HaveBox
{
get => _havebox;
set
{
SetProperty(ref _havebox, value);
}
}
private ObservableCollection<TipHeadItem> tipHeadItems = new ObservableCollection<TipHeadItem>();
public ObservableCollection<TipHeadItem> TipItems
{
get => tipHeadItems;
set => SetProperty(ref tipHeadItems, value);
}
private int useIndex;
/// <summary>
/// 使用的索引位置1-96
/// </summary>
public int UseIndex
{
get { return useIndex; }
set { SetProperty(ref useIndex, value); }
}
private int _boxid_inDosingStation = 0;
/// <summary>
/// 工装在投料站中位置(1-3)
/// </summary>
public int BoxId_inDosingStation
{
get => _boxid_inDosingStation;
set
{
SetProperty(ref _boxid_inDosingStation, value);
}
}
private int pos_inReactStation;
/// <summary>
/// 在反应站Tip头工装位置(1-6中某几个)
/// </summary>
public int Pos_inReactStation
{
get { return pos_inReactStation; }
set { SetProperty(ref pos_inReactStation, value); }
}
private int layer_inRack = 1;
/// <summary>
/// Tip头工装在反应站中的料架的层号 ,默认放置第一层
/// </summary>
public int Layer_inRack
{
get => layer_inRack;
set
{
SetProperty(ref layer_inRack, value);
}
}
private int pos_inRack = 0;
/// <summary>
/// Tip头工装在反应站中的料架的位置1-8
/// </summary>
public int Pos_inRack
{
get => pos_inRack;
set
{
SetProperty(ref pos_inRack, value);
}
}
public TipBoxModel()
{
}
}
}

View File

@ -0,0 +1,113 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Models
{
/// <summary>
/// 2ml瓶-滤芯工装
/// </summary>
[Serializable]
public class _2mlBottleBox_Inner : BaseBottleBox
{
private int _box_inReactRack_layer = 4;
/// <summary>
/// 在设备内部料架的第4层
/// </summary>
public int Box_inReactRack_Layer
{
get { return _box_inReactRack_layer; }
set { SetProperty(ref _box_inReactRack_layer, value); }
}
private int _box_inReactRack_pos;
/// <summary>
/// 在设备内部料架的第几层的第几个位置1-10
/// </summary>
public int Boxd_inReactRack_Pos
{
get { return _box_inReactRack_pos; }
set { SetProperty(ref _box_inReactRack_pos, value); }
}
private int pos_inReactStation;
/// <summary>
/// 在反应工站放料区的6个位置中的其中一个
/// </summary>
public int Pos_InReactStation
{
get { return pos_inReactStation; }
set { SetProperty(ref pos_inReactStation, value); }
}
/// <summary>
/// 从位置1开始使用
/// </summary>
private int useIndex = 1;
public int UseIndex
{
get { return useIndex; }
set { SetProperty(ref useIndex, value); }
}
public _2mlBottleBox_Inner()
{
}
}
/// <summary>
/// 2ml瓶-外瓶工装
/// </summary>
[Serializable]
public class _2mlBottleBox_Outer : BaseBottleBox
{
private int _box_inReactRack_layer = 1;
/// <summary>
/// 在设备内部料架的第1层
/// </summary>
public int Box_inReactRack_Layer
{
get { return _box_inReactRack_layer; }
set { SetProperty(ref _box_inReactRack_layer, value); }
}
private int _box_inReactRack_pos;
/// <summary>
/// 在设备内部料架的第几层的第几个位置1-10
/// </summary>
public int Box_inReactRack_Pos
{
get { return _box_inReactRack_pos; }
set { SetProperty(ref _box_inReactRack_pos, value); }
}
private int pos_inReactStation;
/// <summary>
/// 在反应工站放料区的6个位置中的其中一个
/// </summary>
public int Pos_InReactStation
{
get { return pos_inReactStation; }
set { SetProperty(ref pos_inReactStation, value); }
}
/// <summary>
/// 从位置1开始使用
/// </summary>
private int useIndex = 1;
public int UseIndex
{
get { return useIndex; }
set { SetProperty(ref useIndex, value); }
}
public _2mlBottleBox_Outer()
{
}
}
}

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Common
{
public static class ColorExtensions
{
/// <summary>
/// 将十六进制颜色字符串转换为 System.Windows.Media.Color 对象
/// 支持格式: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
/// </summary>
/// <param name="hex">十六进制颜色字符串</param>
/// <returns>转换后的 Color 对象</returns>
/// <exception cref="ArgumentException">当输入格式无效时抛出</exception>
public static Color FromHex(this string hex)
{
if (string.IsNullOrWhiteSpace(hex))
throw new ArgumentException("颜色字符串不能为空", nameof(hex));
// 移除前缀(如果有)
hex = hex.TrimStart('#');
if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
hex = hex.Substring(2);
// 根据长度解析不同格式
switch (hex.Length)
{
case 3: // RGB
return Color.FromRgb(
ParseComponent(hex[0], hex[0]),
ParseComponent(hex[1], hex[1]),
ParseComponent(hex[2], hex[2]));
case 4: // RGBA
return Color.FromArgb(
ParseComponent(hex[3], hex[3]),
ParseComponent(hex[0], hex[0]),
ParseComponent(hex[1], hex[1]),
ParseComponent(hex[2], hex[2]));
case 6: // RRGGBB
return Color.FromRgb(
ParseComponent(hex[0], hex[1]),
ParseComponent(hex[2], hex[3]),
ParseComponent(hex[4], hex[5]));
case 8: // RRGGBBAA
return Color.FromArgb(
ParseComponent(hex[6], hex[7]),
ParseComponent(hex[0], hex[1]),
ParseComponent(hex[2], hex[3]),
ParseComponent(hex[4], hex[5]));
default:
throw new ArgumentException($"无效的十六进制颜色格式: {hex}", nameof(hex));
}
}
/// <summary>
/// 解析两个十六进制字符为一个字节值
/// </summary>
private static byte ParseComponent(char c1, char c2)
{
return byte.Parse(new string(new[] { c1, c2 }), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
/// <summary>
/// 将 Color 对象转换为十六进制字符串表示(默认格式: #RRGGBB
/// </summary>
/// <param name="color">要转换的颜色</param>
/// <param name="includeAlpha">是否包含透明度通道</param>
/// <returns>十六进制颜色字符串</returns>
public static string ToHexString(this Color color, bool includeAlpha = false)
{
return includeAlpha
? $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"
: $"#{color.R:X2}{color.G:X2}{color.B:X2}";
}
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>disable</Nullable>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\MegaRobo.Common\MegaRobo.ControlDevices\MegaRobo.ControlDevices.csproj" />
<ProjectReference Include="..\MegaRobo.C00225155.Entities\MegaRobo.C00225155.Entities.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="MegaRobo.Entities">
<HintPath>..\..\..\Common\MegaRobo.Common\MegaRobo.Entities\obj\Debug\net7.0\ref\MegaRobo.Entities.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.WpfInfrastructure">
<HintPath>bin\Debug\net7.0-windows\MegaRobo.WpfInfrastructure.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,126 @@
using MegaRobo.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.DevicesModels
{
public abstract class DrainageFluidBaseProp
{
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "分液Z轴驱动位置")]
public uint za { get; set; } = 33700;
//33700 15100..33700 液体出现的绝对位置
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "离开液面时的绝对位置")]
public uint zb { get; set; } = 33700;
// 00000..20000 此参数根据所提供的步骤数定义下面的液体。
[Numeric(Minimum = 0, Maximum = 20000, Default = 200, Descr = "液面跟随步长(Z轴驱动[增量])")]
public uint ze { get; set; } = 200;
// 00000..20000 容器底部的高度从“zh”[增量]测量
[Numeric(Minimum = 0, Maximum = 20000, Default = 0, Descr = "容器底部的高度从“zh”[增量]")]
public uint zk { get; set; }
// 00000..10000 容器底部部分此参数描述了管底部的几何形状图11。gx=10000为平底gx=0为圆锥形底部gx=6180为圆底
[Numeric(Minimum = 0, Maximum = 10000, Default = 10000, Descr = "容器底部几何形状 gx = 10000为平底 0为圆锥形底部 6180为圆底")]
public uint gx { get; set; } = 10000;
//15100 15100..33700 此参数定义了容器底部的z形位置。它对应于最大的浸泡位置。如果LLD模式处于激活状态则将搜索液位直到此位置图12
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "容器底部Z值")]
public uint zh { get; set; } = 15200;
//33700 15100..33700 结束遍历位置。在命令结尾处的横向位置。
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "结束遍历位置")]
public uint zg { get; set; } = 33700;
// 0000..9999 运输空气量[增量]空气在吸入或分配结束时被吸入,并在接下来的分配步骤中自动作为额外的量再次分配。它受到尖端几何形状和液体蒸汽压的影响。防止在运输过程中产生液滴的形成。这个参数在移液挥发性物质时很有用。
[Numeric(Minimum = 0, Maximum = 9999, Default = 200, Descr = "运输空气量[增量]")]
public uint dg { get; set; } = 200;
//public uint zq { get; set; } = 280;
// 000..999 吸入后在液体中停留的时间[1/10秒]此参数设置吸入后尖端或离开液体前在液体中停留的时间。在设置该参数时,考虑了尖端的几何形状和液体粘度。如果沉淀时间太短,液体没有足够的时间来填充尖端,一旦尖端离开液体,不需要的空气就会被吸入。
[Numeric(Minimum = 0, Maximum = 999, Default = 10, Name = "ML", Index = 9, Descr = "吸入后在液体中停留的时间[1/10秒]")]
public uint to { get; set; } = 10;
//22350 00020..56000 流量,【增加量/秒】。该参数对应于吸入或分配期间的柱塞速度。流量是最关键的参数,受尖端几何形状,液体粘度、密度和蒸汽压力:小尖端开放->慢流速,高粘度->慢流速,高密度->慢流速,高蒸汽压->快速流速
[Numeric(Minimum = 20, Maximum = 56000, Default = 22350, Name = "ML", Index = 10, Descr = "分液速度,流量,【增加量/秒】")]
public uint dv { get; set; } = 22350;
//5600 00000..56000 停止流量停止分配的柱塞的分配速度。如果等于“流量”,则柱塞突然停止而不减速。如果设置为最小值,柱塞的运动会逐渐减慢,直到停止。移液粘性液体如甘油时,建议逐步停止(缓慢停止流量)以完成分配。
//[Numeric(Minimum = 00, Maximum = 56000, Default = 5600, Name = "MH", Index = 21, Descr = "停止流量停止分配的柱塞的分配速度")]
[Numeric(Minimum = 00, Maximum = 56000, Default = 5600, Name = "ML", Index = 11, Descr = "停止流量,停止分配的柱塞的分配速度")]
public uint du { get; set; } = 5600;
//400 005..600 加速分配驱动器[1000增量/秒2]设置分配期间柱塞的加速度。需要控制流量切断。防止在尖端孔口处形成液滴
//[Numeric(Minimum = 5, Maximum = 600, Default = 400, Name = "MH", Index = 22, Descr = "加速分配驱动器[1000增量/秒2]")]
[Numeric(Minimum = 5, Maximum = 600, Default = 400, Name = "ML", Index = 12, Descr = "加速分配驱动器[1000增量/秒2]")]
public uint dr { get; set; } = 400;
//1000 00020..15000 交换速度[增量/秒]决定了抽吸或分配后尖端从液体中抽出的速度。该参数是根据液体的粘度和蒸汽压来选择的。使用较慢的速度可以防止粘性液体粘附在尖端的外部。
//[Numeric(Minimum = 20, Maximum = 15000, Default = 1000, Name = "MH", Index = 23, Descr = "抽吸或分配后尖端从液体中抽出的速度交换速度[增量/秒]")]
[Numeric(Minimum = 20, Maximum = 15000, Default = 5000, Name = "ML", Index = 13, Descr = "抽吸或分配后尖端从液体中抽出的速度交换速度[增量/秒]")]
public uint zu { get; set; } = 5000;
}
//分液(清空用ML一吸多分用MH)
/// <summary>
/// 分液清空
/// </summary>
public class DrainageFluidByML : DrainageFluidBaseProp
{
public string CmdCode = "ML";
}
/// <summary>
/// 一吸多分
/// </summary>
public class DrainageFluidByMH : DrainageFluidBaseProp
{
public string CmdCode = "MH";
// 000000..125400 分液量
[Numeric(Minimum = 0, Maximum = 125400, Default = 0, Name = "MH", Index = 1, Descr = "分液量")]
public uint db { get; set; }
// 000..999 停止后退容积【增量】分配后立即再次吸入该空气量,以突然切断液体流动(仅限喷射部分模式)。移液乙醇等挥发性液体时防止液滴形成。
[Numeric(Minimum = 0, Maximum = 999, Default = 0, Name = "MH", Index = 2, Descr = "停止后退容积")]
public uint dd { get; set; }
//0..1 防液滴控制ADC0=关闭1=开启。在使用具有低粘度和高蒸汽压的移液液体时应使用ADC如乙醇。如果压力达到可能发生液滴的临界值则尽可能多地抽出柱塞以降低压力并防止液滴的形成。
[Numeric(Minimum = 0, Maximum = 1, Default = 0, Name = "MH", Index = 12, Descr = "防液滴控制ADC0=关闭1=开启)")]
public uint bl { get; set; } = 1;
// 000000..125400 混合体积[增量】这是混合探头所需的体积。
[Numeric(Minimum = 0, Maximum = 125400, Default = 0, Name = "MH", Index = 13, Descr = "混合体积[增量】这是混合探头所需的体积。")]
public uint dm { get; set; }
// 00..99 混合循环。混合循环次数。
[Numeric(Minimum = 0, Maximum = 99, Default = 0, Name = "MH", Index = 14, Descr = "混合循环。混合循环次数。")]
public uint dn { get; set; }
// 00000..20000 表面进行混合体积的以下步骤。请参见“zd”参数的说明
[Numeric(Minimum = 0, Maximum = 20000, Default = 0, Name = "MH", Index = 15, Descr = "表面进行混合体积的以下步骤。请参见“zd”参数的说明")]
public uint zy { get; set; }
//22350 00020..56000 混合流量这是混合探头时的流量。
[Numeric(Minimum = 20, Maximum = 56000, Default = 0, Name = "MH", Index = 16, Descr = "混合流量这是混合探头时的流量。")]
public uint _do { get; set; } = 22350;
//0..1 设置搜索底部模式 (zm0 = off, zm1 = on)
[Numeric(Minimum = 0, Maximum = 1, Default = 0, Name = "MH", Index = 17, Descr = "设置搜索底部模式 (zm0 = off, zm1 = on)")]
public uint zm { get; set; } = 0;
//186 000..500 开始高度底部搜索开始位置=参数zh+参数zx
[Numeric(Minimum = 0, Maximum = 500, Default = 0, Name = "MH", Index = 18, Descr = "开始高度底部搜索开始位置=参数zh+参数zx")]
public uint zx { get; set; } = 186;
// 0000..9999 底部搜索后的分配高度
[Numeric(Minimum = 0, Maximum = 9999, Default = 0, Name = "MH", Index = 19, Descr = "底部搜索后的分配高度")]
public uint zo { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using CommunityToolkit.Mvvm.ComponentModel;
using MegaRobo.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Common.DevicesModels
{
public class PropertyItem : ObservableObject
{
private object _value;
public object Value
{
get => _value;
set
{
if (SetProperty(ref _value, value)) // SetProperty 会触发 PropertyChanged
{
// 值变化时,更新 SuckFluidProp 对应属性
PropertyInfo?.SetValue(TargetObject, Convert.ChangeType(value, Type));
}
}
}
// 其他属性(保持不变)
public string Name { get; set; }
public string Description { get; set; }
public Type Type { get; set; }
public NumericAttribute NumericAttribute { get; set; }
public object TargetObject { get; set; }
public PropertyInfo PropertyInfo { get; set; }
//// 通知属性变化
//public event PropertyChangedEventHandler PropertyChanged;
//protected void OnPropertyChanged([CallerMemberName] string propName = null)
//{
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
//}
}
}

View File

@ -0,0 +1,232 @@
using MegaRobo.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.DevicesModels
{
public enum SuckMode
{
[Description("电容式")]
MA,
[Description("电压式")]
ME,
[Description("固定高度")]
MG,
}
public enum DrainageMode
{
[Description("排空")]
ML,
[Description("一吸多分")]
MH,
}
public abstract class SuckFluidBaseProp
{
// 000000-125400 吸液量
[Numeric(Minimum = 0, Maximum = 125400, Descr = "吸液量ul")]
public uint da { get; set; }
// 0000-9999 过度吸气量[增量]
[Numeric(Minimum = 0, Maximum = 9999, Descr = "过度吸气量")]
public uint dc { get; set; } = 100;
// 00000-20000 液面以下高度
[Numeric(Minimum = 0, Maximum = 20000, Descr = "吸液跟随下降高度")]
public uint zd { get; set; } = 200;
// 00000..20000 容器底部的高度
[Numeric(Minimum = 0, Maximum = 20000, Descr = "容器底部几何形状的高度 平底为 0 ")]
public uint zk { get; set; } = 0; //600
// 00000..10000 容器底部的部分。此参数描述了管底部的几何形状图11。gx=10000为平底gx=0为圆锥形底部gx=6180为圆底。
[Numeric(Minimum = 0, Maximum = 10000, Default =10000, Descr = "容器底部的部分 gx=10000为平底 0为圆锥形底部 6180为圆底")]
public uint gx { get; set; } = 10000; //6180;
//15200 15100..33700 容器底部的位置。
[Numeric(Minimum = 15200, Maximum = 33700, Descr = "容器底部位置")]
public uint zh { get; set; } = 15200;
//33700 15100..33700 LLD搜索位置
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "LLD搜索位置,吸液位置")]
public uint zc { get; set; } = 26000;
// 0000..9999 渗透深度增加了液体表面以下尖端的渗透深度。
[Numeric(Minimum = 0, Maximum = 9999, Default =8, Descr = "浸入液面高度")]
public uint zi { get; set; } = 8U; //探入液面高度Tip头进入液面
//33700 15100..33700 结束遍历位置。在命令结尾处的横向位置。
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "结束遍历位置")]
public uint zg { get; set; } = 33700;
//0 000000..125400 排气空气量在吸入过程中首先吸收该空气量。它在分配结束时被吹掉。它受到液体粘度和蒸气压的影响。爆裂的体积有助于完全清空尖端
[Numeric(Minimum = 0, Maximum = 125400, Default = 0, Descr = "排气空气量在吸入过程中首先吸收该空气量")]
public uint df { get; set; } = 500;
//0 0000..9999 运输空气量[增量]空气在吸入或分配结束时被吸入,并在接下来的分配步骤中自动作为额外的量再次分配。它受到尖端几何形状和液体蒸汽压的影响。防止在运输过程中产生液滴的形成。这个参数在移液挥发性物质时很有用。
[Numeric(Minimum = 0, Maximum = 9999, Descr = "转移空气量")]
public uint dg { get; set; } = 200;
//280 0000..9999 离开高度[增加*]高度超过液体表面,在此期间,宙斯驱动缓慢地穿透或离开液体。
[Numeric(Minimum = 0, Maximum = 9999, Default = 280, Descr = "离开液面高度")]
public uint zq { get; set; } = 280;
// 000..999 吸入后的液体中沉淀时间[1/10秒]。该参数设置抽吸或离开液体前在液体中停留的时间。在设置该参数时,考虑了尖端的几何形状和液体粘度。如果沉淀时间太短,液体没有足够的时间来填充尖端,一旦尖端离开液体,不需要的空气就会被吸入。
[Numeric(Minimum = 0, Maximum = 999, Descr = "吸入后的液体中沉淀时间[1/10秒]")]
public uint to { get; set; } = 10;
//0..1 吸气模式(dj0=简单dj1=空杯)
[Numeric(Minimum = 0, Maximum = 1, Descr = "吸气模式(dj0=简单dj1=空杯)")]
public uint dj { get; set; } = 0;
//0..1 防液滴控制ADC0=关闭1=开启。在使用具有低粘度和高蒸汽压的移液液体时应使用ADC如乙醇。如果压力达到可能发生滴落的临界值则尽可能多地抽出柱塞以降低压力并防止液滴的形成。
[Numeric(Minimum = 0, Maximum = 1, Descr = "防液滴控制ADC0=关闭1=开启)")]
public uint bl { get; set; } = 0;
//0..1 堵塞检测
[Numeric(Minimum = 0, Maximum = 1, Descr = "堵塞检测")]
public uint gq { get; set; } = 0;
//1..4 CLLD灵敏度1=非常高高2=3=培养基低4=)。灵敏度可以调整到容器尺寸和要检测的液体体积(小体积->高灵敏度)。
//public uint cs ps {get;set;}
// 000000..125400 混合体积的增量。这是混合探针所需的体积。
[Numeric(Minimum = 0, Maximum = 125400, Descr = "混合体积的增量")]
public uint dm { get; set; }
//00..99 混合循环。混合循环次数。
[Numeric(Minimum = 0, Maximum = 99, Descr = "混合循环次数")]
public uint dn { get; set; }
// 00000..20000 表面进行混合体积的以下步骤。请参见“zd”参数的描述。
[Numeric(Minimum = 0, Maximum = 20000, Descr = "表面进行混合体积的以下步骤")]
public uint zy { get; set; }
//22350 00020..56000 混合流量。这是探头混合时的流量。
[Numeric(Minimum = 20, Maximum = 56000, Default = 22350, Descr = "混合流量")]
public uint _do { get; set; } = 22350; //22350
//22350 00020..56000 该参数对应于吸入或分配期间的柱塞速度。流量是最关键的参数,受尖端几何形状,液体粘度、密度和蒸汽压力:小尖端开放->慢流速,高粘度->慢流速,高密度->慢流速,高蒸汽压->快速流速。
[Numeric(Minimum = 20, Maximum = 56000, Default = 22350, Descr = "吸入速度1该参数对应于吸入或分配期间的柱塞速度")]
public uint dv { get; set; } = 22350;
//1000 00020..15000 交换速度[增量/秒]决定了抽吸或分配后尖端从液体中抽出的速度。该参数是根据液体的粘度和蒸汽压来选择的。使用较慢的速度可以防止粘性液体粘附在尖端的外部。
[Numeric(Minimum = 20, Maximum = 15000, Default = 1000, Descr = "抽吸或分配后尖端从液体中抽出的速度交换速度[增量/秒]")]
public uint zu { get; set; } = 1000; // 2000;
}
/// <summary>
/// 通过电容式LLD在液体表面进行搜索的抽吸液体
/// </summary>
public class SuckFluidByMA : SuckFluidBaseProp
{
public string CmdCode => "MA";
//1..4 CLLD灵敏度1=非常高高2=3=培养基低4=)。灵敏度可以调整到容器尺寸和要检测的液体体积(小体积->高灵敏度)。
[Numeric(Minimum = 1, Maximum = 4, Default = 2, Descr = "CLLD灵敏度")]
public uint cs { get; set; } = 1;
}
/// <summary>
/// 通过压力式LLD在液体表面进行搜索的抽吸液体
/// </summary>
public class SuckFluidByME : SuckFluidBaseProp
{
public string CmdCode => "ME";
//1..4 pLLD灵敏度1=非常高高2=3=一般低4=)。灵敏度可以调整到容器尺寸和要检测的液体体积(小体积->高灵敏度)。
[Numeric(Minimum = 1, Maximum = 4, Default = 2, Descr = "pLLD灵敏度(压力探测灵敏度)")]
public uint ps { get; set; } = 1;
}
/// <summary>
/// 固定位置吸液
/// </summary>
public class SuckFluidByMG
{
public string CmdCode => "MG";
[Numeric(Minimum = 0, Maximum = 125400, Descr = "吸液量")]
public uint da { get; set; }
[Numeric(Minimum = 0, Maximum = 9999, Default =100, Descr = "过度吸气量")]
public uint dc { get; set; } = 100;
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "吸液时Z轴高度")]
public uint za { get; set; } = 33700;
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "离开液面时的绝对位置")]
public uint zb { get; set; } = 33700;
[Numeric(Minimum = 0, Maximum = 20000, Default = 200, Descr = "吸液跟随下降高度")]
public uint zd { get; set; } = 200;
[Numeric(Minimum = 0, Maximum = 20000, Default = 0, Descr = "容器尾部几何形状与底部的高度 平底为0")]
public uint zk { get; set; } = 0;
//00000..10000 容器底部的部分。此参数描述了管底部的几何形状图11。gx=10000为平底gx=0为圆锥形底部gx=6180为圆底。
[Numeric(Minimum = 0, Maximum = 10000,Default =10000, Descr = "容器底部的部分 gx=10000为平底 0为圆锥形底部 6180为圆底")]
public uint gx { get; set; } = 10000; //6180;
//15200 15100..33700 容器底部的位置。
[Numeric(Minimum = 15200, Maximum = 33700, Descr = "容器底部位置")]
public uint zh { get; set; } = 15200;
//33700 15100..33700 结束遍历位置。在命令结尾处的横向位置。
[Numeric(Minimum = 15200, Maximum = 33700, Default = 33700, Descr = "结束遍历位置")]
public uint zg { get; set; } = 33700;
//0 000000..125400 排气空气量在吸入过程中首先吸收该空气量。它在分配结束时被吹掉。它受到液体粘度和蒸气压的影响。爆裂的体积有助于完全清空尖端
[Numeric(Minimum = 0, Maximum = 125400, Default = 500, Descr = "排气空气量在吸入过程中首先吸收该空气量")]
public uint df { get; set; } = 500;
//0 0000..9999 运输空气量[增量]空气在吸入或分配结束时被吸入,并在接下来的分配步骤中自动作为额外的量再次分配。它受到尖端几何形状和液体蒸汽压的影响。防止在运输过程中产生液滴的形成。这个参数在移液挥发性物质时很有用。
[Numeric(Minimum = 0, Maximum = 9999,Default =200, Descr = "转移空气量")]
public uint dg { get; set; } = 200;
// 000..999 吸入后的液体中沉淀时间[1/10秒]。该参数设置抽吸或离开液体前在液体中停留的时间。在设置该参数时,考虑了尖端的几何形状和液体粘度。如果沉淀时间太短,液体没有足够的时间来填充尖端,一旦尖端离开液体,不需要的空气就会被吸入。
[Numeric(Minimum = 0, Maximum = 999,Default =10, Descr = "吸入后的液体中沉淀时间[1/10秒]")]
public uint to { get; set; } = 10;
//0..1 吸气模式(dj0=简单dj1=空杯)
[Numeric(Minimum = 0, Maximum = 1, Descr = "吸气模式(dj0=简单dj1=空杯)")]
public uint dj { get; set; } = 0;
//0..1 防液滴控制ADC0=关闭1=开启。在使用具有低粘度和高蒸汽压的移液液体时应使用ADC如乙醇。如果压力达到可能发生滴落的临界值则尽可能多地抽出柱塞以降低压力并防止液滴的形成。
[Numeric(Minimum = 0, Maximum = 1, Descr = "防液滴控制ADC0=关闭1=开启)")]
public uint bl { get; set; } = 0;
//0..1 堵塞检测
[Numeric(Minimum = 0, Maximum = 1, Descr = "堵塞检测")]
public uint gq { get; set; }
// 000000..125400 混合体积的增量。这是混合探针所需的体积。
[Numeric(Minimum = 0, Maximum = 125400, Descr = "混合体积的增量")]
public uint dm { get; set; }
//00..99 混合循环。混合循环次数。
[Numeric(Minimum = 0, Maximum = 99, Descr = "混合循环次数")]
public uint dn { get; set; }
// 00000..20000 表面进行混合体积的以下步骤。请参见“zd”参数的描述。
[Numeric(Minimum = 0, Maximum = 20000, Descr = "表面进行混合体积的以下步骤")]
public uint zy { get; set; }
//22350 00020..56000 混合流量。这是探头混合时的流量。
[Numeric(Minimum = 20, Maximum = 56000, Default = 22350, Descr = "混合流量")]
public uint _do { get; set; } = 22350; //22350
//22350 00020..56000 该参数对应于吸入或分配期间的柱塞速度。流量是最关键的参数,受尖端几何形状,液体粘度、密度和蒸汽压力:小尖端开放->慢流速,高粘度->慢流速,高密度->慢流速,高蒸汽压->快速流速。
[Numeric(Minimum = 20, Maximum = 56000, Default = 22350, Descr = "该参数对应于吸入或分配期间的柱塞速度")]
public uint dv { get; set; } = 22350;
//1000 00020..15000 交换速度[增量/秒]决定了抽吸或分配后尖端从液体中抽出的速度。该参数是根据液体的粘度和蒸汽压来选择的。使用较慢的速度可以防止粘性液体粘附在尖端的外部。
[Numeric(Minimum = 20, Maximum = 15000, Default = 5000, Descr = "抽吸或分配后尖端从液体中抽出的速度交换速度[增量/秒]")]
public uint zu { get; set; } = 5000; // 2000;
}
}

View File

@ -0,0 +1,438 @@
using Common.Models;
using Microsoft.Win32;
using Newtonsoft;
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows;
using System.Windows.Media;
using System.Xml.Serialization;
namespace Common
{
public class File_Operator
{
private static readonly object _lock = new object();
public static T Xml_Serialize<T>(string strFile, bool bSave, T param, out bool bReadOK) where T : class
{
lock (_lock)
{
bReadOK = true;
XmlSerializer xs = null;
// 针对 BaseBottleBox 集合,显式指定所有派生类
if (typeof(T) == typeof(ProjectProperty))
{
Type[] extraTypes = new Type[]
{
typeof(BaseBottleBox),
typeof(SampleBottleBoxModel),
typeof(SourceLiquidBottleBoxModel),
typeof(SourcePowderBottleBoxModel),
typeof(SourcePowderBottleBoxModel_125ml),
typeof(TipBoxModel),
typeof(SourcePowderBottleModel),
typeof(TipHeadItem)
};
xs = new XmlSerializer(typeof(ProjectProperty), extraTypes);
}
else
{
xs = new XmlSerializer(param.GetType());
}
try
{
// 使用 using 自动释放流(避免资源泄漏)
if (bSave)
{
using (Stream stream = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.Read))
{
xs.Serialize(stream, param);
}
}
else
{
if (File.Exists(strFile))
{
using (Stream stream = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
param = xs.Deserialize(stream) as T;
}
return param;
}
else
{
// 文件不存在时,先创建并序列化默认值
using (Stream stream = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.Read))
{
xs.Serialize(stream, param);
}
// 重新读取
using (Stream stream = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
param = xs.Deserialize(stream) as T;
}
return param;
}
}
}
catch (Exception ex)
{
bReadOK = false;
MessageBox.Show($"序列化失败:{ex.Message}\n{ex.StackTrace}", "错误");
}
return null;
}
}
/// <summary>
/// XML文件读写
/// 保存配置参数 bReadOK==false为发生异常
/// </summary>
/// <typeparam name="T">类类型</typeparam>
/// <param name="strFile">完整文件路径名</param>
/// <param name="bSave">true:保存false:读取</param>
/// <param name="param">要进行序列化或反序列化的对象实例</param>
/// <param name="bReadOK">是否读写成功,有异常</param>
/// <returns>读取的数据</returns>
//public static T Xml_Serialize<T>(string strFile, bool bSave, T param, out bool bReadOK) where T : class
//{
// lock (_lock)
// {
// bReadOK = true;
// XmlSerializer xs = new XmlSerializer(param.GetType());
// try
// {
// if (bSave)
// {
// //创建一个 FileStream 对象,以 FileMode.Create 模式打开文件,用于写入数据。
// //使用 xs.Serialize 方法将 param 对象序列化为 XML 数据,并写入文件。
// //关闭文件流。
// Stream stream = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.Read);
// xs.Serialize(stream, param);
// stream.Close();
// }
// else
// {
// //检查文件是否存在。
// //如果文件存在,创建一个 FileStream 对象,以 FileMode.Open 模式打开文件,用于读取数据。
// //使用 xs.Deserialize 方法将文件中的 XML 数据反序列化为 T 类型的对象,并赋值给 param。
// //关闭文件流,并返回反序列化后的对象。
// if (File.Exists(strFile))
// {
// Stream stream = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.Read);
// param = xs.Deserialize(stream) as T;
// stream.Close();
// return param;
// }
// else
// {
// //如果文件不存在,先创建一个 FileStream 对象,以 FileMode.Create 模式打开文件,将 param 对象序列化为 XML 数据并写入文件。
// //再次打开文件,读取其中的 XML 数据进行反序列化,并返回反序列化后的对象。
// Stream stream = new FileStream(strFile, FileMode.Create, FileAccess.Write, FileShare.Read);
// xs.Serialize(stream, param);
// stream.Close();
// stream = new FileStream(strFile, FileMode.Open, FileAccess.Read, FileShare.Read);
// param = xs.Deserialize(stream) as T;
// stream.Close();
// return param;
// }
// }
// }
// catch (Exception ex)
// {
// bReadOK = false;
// MessageBox.Show(ex.Message);
// }
// return null;
// }
//}
/// <summary>
/// 通过文件名寻找同目录下的其他同类型文件
/// </summary>
/// <param name="file">文件的绝对路径名</param>
public static List<string> GetFile(string file)
{
List<string> listFile = new List<string>();
if (!string.IsNullOrEmpty(file) && File.Exists(file))
{
int index = file.LastIndexOf("\\");
if (-1 == index) return null;
string path = file.Substring(0, index);//文件所在文件夹路径
string name = file.Substring(index);//文件名
string postfix = "";//取出后缀名
if (name.Contains("."))
{
postfix = name.Substring(name.LastIndexOf('.'));//例: .txt
}
else
{
return null;
}
string[] files = null;
try
{
files = Directory.GetFiles(path);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return null;
}
foreach (var item in files)
{
int nIndex = item.IndexOf(postfix);
if (nIndex != -1)
{
listFile.Add(item);
}
}
}
return listFile;
}
#region
//public static void CreatRegistryPath(string strPathName)
//{
// try
// {
// RegistryKey key = Registry.LocalMachine;
// //RegistryKey software = key.CreateSubKey("software\\test");
// RegistryKey software = key.CreateSubKey("software\\" + strPathName);
// key.Close();
// }
// catch (Exception ex)
// {
// MessageBox.Show("对注册表创建路径时出现异常!" + ex.Message);
// }
//}
//public static void DeleteRegistryPath(string strPathName)
//{
// try
// {
// RegistryKey key = Registry.LocalMachine;
// //key.DeleteSubKey("software\\test", true);
// key.DeleteSubKey("software\\" + strPathName, true);
// key.Close();
// }
// catch (Exception ex)
// {
// MessageBox.Show("对注册表删除路径时出现异常!" + ex.Message);
// }
//}
//public static void SetRegistryValue(string strPathName, string strKey, string strValue)
//{
// try
// {
// RegistryKey key = Registry.LocalMachine;
// RegistryKey software = key.OpenSubKey("software\\" + strPathName, true);
// //software.SetValue("test", "博客园");
// software.SetValue(strKey, strValue);
// key.Close();
// }
// catch (Exception ex)
// {
// MessageBox.Show("对注册表设置键值时出现异常!" + ex.Message);
// }
//}
//public static string GetRegistryValue(string strPathName, string strKey)
//{
// try
// {
// RegistryKey Key = Registry.LocalMachine;
// RegistryKey myreg = Key.OpenSubKey("software\\" + strPathName, true);
// //info = myreg.GetValue("test").ToString();
// string strValue = myreg.GetValue(strKey).ToString();
// myreg.Close();
// return strValue;
// }
// catch (Exception ex)
// {
// MessageBox.Show("对注册表获取值时出现异常!" + ex.Message);
// return "";
// }
//}
//public static void DeleteRegistryKey(string strPathName, string strKey)
//{
// try
// {
// RegistryKey delKey = Registry.LocalMachine.OpenSubKey("Software\\" + strPathName, true);
// //delKey.DeleteValue("test");
// delKey.DeleteValue(strKey);
// delKey.Close();
// }
// catch (Exception ex)
// {
// MessageBox.Show("对注册表删除键值时出现异常!" + ex.Message);
// }
//}
#endregion
#region
public static T DeepCopy<T>(T obj)
{
if (obj == null) return default;
//var json = JsonConvert.SerializeObject(obj);
//return JsonConvert.DeserializeObject<T>(json);
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string json = JsonConvert.SerializeObject(obj, settings);
return JsonConvert.DeserializeObject<T>(json, settings);
}
[Obsolete("弃用使用上面的DeepCopy方法")]
public static T DeepCopy_1<T>(T obj)
{
if (obj == null) return default;
Type actualType = obj.GetType();
// 处理值类型和字符串
// 处理值类型和字符串
if (actualType.IsValueType || actualType == typeof(string))
return obj;
// 处理数组
if (actualType.IsArray)
{
var elementType = typeof(T).GetElementType();
var sourceArray = (Array)(object)obj;
var copiedArray = Array.CreateInstance(elementType, sourceArray.Length);
for (int i = 0; i < sourceArray.Length; i++)
{
copiedArray.SetValue(DeepCopy((dynamic)sourceArray.GetValue(i)), i);
}
return (T)(object)copiedArray;
}
// 处理集合(如 List, ObservableCollection
// 处理集合(优先直接创建目标类型,避免冗余转换)
if (typeof(IEnumerable).IsAssignableFrom(actualType) && actualType != typeof(string))
{
return DeepCopyCollection<T>(obj);
}
// 处理普通对象(核心修改)
// 关键修改2按“实际类型”创建实例而非 T 的声明类型)
object copy = Activator.CreateInstance(actualType);
// 关键修改3拷贝所有字段包括私有字段+派生类特有字段)
FieldInfo[] fields = actualType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
if (field.IsStatic) continue; // 跳过静态字段
var value = field.GetValue(obj);
// 递归深拷贝字段值(保持原逻辑)
var copiedValue = DeepCopy((dynamic)value);
field.SetValue(copy, copiedValue);
}
// 强制转换为 T因为实际类型是 T 的派生类,转换安全)
return (T)copy;
}
/// <summary>
/// 专门处理 ObservableCollection 类型属性(支持只读属性)
/// </summary>
private static void DeepCopyObservableCollectionProperty(PropertyInfo property, object sourceObj, object targetObj)
{
// 获取源集合和元素类型
var sourceCollection = property.GetValue(sourceObj) as IEnumerable;
var elementType = property.PropertyType.GetGenericArguments()[0];
// 创建新的 ObservableCollection 实例(匹配原属性类型)
var targetCollection = Activator.CreateInstance(property.PropertyType) as IList;
if (targetCollection == null)
throw new InvalidOperationException($"无法创建 {property.PropertyType.Name} 实例");
// 深拷贝集合元素并添加到新集合
foreach (var item in sourceCollection)
{
var copiedItem = DeepCopy((dynamic)item);
targetCollection.Add(copiedItem);
}
// 给目标对象赋值:可写属性直接 SetValue只读属性通过底层字段赋值
if (property.CanWrite)
{
property.SetValue(targetObj, targetCollection);
}
else
{
// 匹配只读属性的底层字段常见命名规范_propertyName 或 m_propertyName
var fieldName = $"_{char.ToLower(property.Name[0])}{property.Name.Substring(1)}";
var field = targetObj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
// 若未匹配到,尝试 mPropertyName 规范
if (field == null)
{
fieldName = $"m{property.Name}";
field = targetObj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
}
if (field != null && field.FieldType == property.PropertyType)
{
field.SetValue(targetObj, targetCollection);
}
else
{
throw new InvalidOperationException($"无法给只读属性 {property.DeclaringType.Name}.{property.Name} 赋值,请检查字段命名(预期:{fieldName}");
}
}
}
/// <summary>
/// 优化集合深拷贝:直接创建目标类型集合,避免 List 中转
/// </summary>
private static T DeepCopyCollection<T>(T sourceCollection)
{
var sourceType = typeof(T);
var elementType = sourceType.IsGenericType ? sourceType.GetGenericArguments()[0] : typeof(object);
// 直接创建目标类型的集合实例(如 ObservableCollection<SampleBottleModel>
var targetCollection = Activator.CreateInstance(sourceType) as IList;
if (targetCollection == null)
throw new InvalidOperationException($"无法创建集合类型 {sourceType.Name} 实例");
// 深拷贝每个元素并添加到新集合
foreach (var item in (IEnumerable)sourceCollection)
{
var copiedItem = DeepCopy((dynamic)item);
targetCollection.Add(copiedItem);
}
return (T)targetCollection;
}
public static ObservableCollection<T> DeepCopyObservableCollection<T>(ObservableCollection<T> source)
{
// 序列化为 JSON 字符串
var json = JsonConvert.SerializeObject(source);
// 从 JSON 字符串反序列化为新对象
return JsonConvert.DeserializeObject<ObservableCollection<T>>(json);
}
#endregion
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Models
{
public class UseLiquidBottle
{
public BaseBottleBox liquidbottleFixture { get; set; } = null;
public SourceLiquidBottleModel liquidbottle { get; set; } = null;
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class ObstructFlag
{
/// <summary>
/// 命名
/// </summary>
public string strName = "功能描述";
/// <summary>
/// true/false
/// </summary>
public bool flag = true;
}
}

View File

@ -0,0 +1,201 @@
using Common.DevicesModels;
using Common.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Xml.Serialization;
namespace Common
{
[Serializable]
public class ProjectProperty : ObservableObject
{
private SampleBottleTypeEnum sampleBottleType = SampleBottleTypeEnum._5mL;
public SampleBottleTypeEnum SampleBottleType
{
get => sampleBottleType;
set => SetProperty(ref sampleBottleType, value);
}
private ObservableCollection<BaseBottleBox> _tansferArea = new ObservableCollection<BaseBottleBox>();
/// <summary>
/// 投料站 7个传送工装位置
/// </summary>
[XmlArray("TransferArea")] // 序列化后的XML节点名
[XmlArrayItem("BaseBottleBox", Type = typeof(BaseBottleBox))] // 基类元素
[XmlArrayItem("SampleBottleBoxModel", Type = typeof(SampleBottleBoxModel))] // 派生类元素
[XmlArrayItem("SourceLiquidBottleBoxModel", Type = typeof(SourceLiquidBottleBoxModel))]
[XmlArrayItem("SourcePowderBottleBoxModel", Type = typeof(SourcePowderBottleBoxModel))]
[XmlArrayItem("SourcePowderBottleBoxModel_125ml", Type = typeof(SourcePowderBottleBoxModel_125ml))]
[XmlArrayItem("TipBoxModel", Type = typeof(TipBoxModel))]
public ObservableCollection<BaseBottleBox> TransferArea
{
get => _tansferArea;
set => SetProperty(ref _tansferArea, value);
}
private ObservableCollection<BaseBottleBox> _fixtureCacheArea=new ObservableCollection<BaseBottleBox>();
/// <summary>
/// 投料站8个治具缓存区 ,目前26由于干涉取消但是程序里还保留
/// </summary>
[XmlArray("FixtureCacheArea")] // 序列化后的XML节点名
[XmlArrayItem("BaseBottleBox", Type = typeof(BaseBottleBox))] // 基类元素
[XmlArrayItem("SampleBottleBoxModel", Type = typeof(SampleBottleBoxModel))] // 派生类元素
[XmlArrayItem("SourceLiquidBottleBoxModel", Type = typeof(SourceLiquidBottleBoxModel))]
[XmlArrayItem("SourcePowderBottleBoxModel", Type = typeof(SourcePowderBottleBoxModel))]
[XmlArrayItem("SourcePowderBottleBoxModel_125ml", Type = typeof(SourcePowderBottleBoxModel_125ml))]
[XmlArrayItem("TipBoxModel", Type = typeof(TipBoxModel))]
public ObservableCollection<BaseBottleBox> FixtureCacheArea
{
get => _fixtureCacheArea;
set
{
SetProperty(ref _fixtureCacheArea, value);
}
}
private ObservableCollection<SourcePowderBottleModel> powderheadcacheArea = new ObservableCollection<SourcePowderBottleModel>();
/// <summary>
/// 投料站粉末头缓存区 10个位置
/// </summary>
[XmlArray("PowderHeaderCacheArea")]
[XmlArrayItem("SourcePowderBottleModel")]
public ObservableCollection<SourcePowderBottleModel> PowderHeaderCacheArea
{
get { return powderheadcacheArea; }
set { SetProperty(ref powderheadcacheArea, value); }
}
private ObservableCollection<TipBoxModel> tipHeadArea = new ObservableCollection<TipBoxModel>();
/// <summary>
/// 投料站 TIP头放置区域 3个位置
/// </summary>
[XmlArray("TipHeadArea")]
[XmlArrayItem("TipBoxModel")]
public ObservableCollection<TipBoxModel> TipHeadArea
{
get { return tipHeadArea; }
set { SetProperty(ref tipHeadArea, value); }
}
private bool bnouseBS;
/// <summary>
/// 不启用BS false:启用BS true:不启用BS
/// </summary>
public bool bNoUseBS
{
get { return bnouseBS; }
set { SetProperty(ref bnouseBS, value); }
}
#region
private List<_2mlBottleBox_Outer> _2mlbottlebox_outer = new List<_2mlBottleBox_Outer>();
/// <summary>
/// 料架上第五层的 2ml外瓶工装集合
/// </summary>
public List<_2mlBottleBox_Outer> Rack_2mlBottleBox_OuterList
{
get { return _2mlbottlebox_outer; }
set { _2mlbottlebox_outer = value; }
}
private List<_2mlBottleBox_Inner> _2mlbottlebox_inner = new List<_2mlBottleBox_Inner>();
/// <summary>
/// 料架上第四层的 2ml滤芯工装集合
/// </summary>
public List<_2mlBottleBox_Inner> Rack_2mlBottleBox_InnerList
{
get { return _2mlbottlebox_inner; }
set { _2mlbottlebox_inner = value; }
}
private List<SampleBottleBoxModel> _5mlsamplebottlebox = new List<SampleBottleBoxModel>();
/// <summary>
/// 料架上第三层的 5ml样品工装集合
/// </summary>
public List<SampleBottleBoxModel> Rack_5mlSampleBottleBoxList
{
get { return _5mlsamplebottlebox; }
set { _5mlsamplebottlebox = value; }
}
private List<SampleBottleBoxModel> _12mlsamplebottlebox = new List<SampleBottleBoxModel>();
/// <summary>
/// 料架上第二层的 12ml样品工装集合
/// </summary>
public List<SampleBottleBoxModel> Rack_12mlSampleBottleBoxList
{
get { return _12mlsamplebottlebox; }
set { _12mlsamplebottlebox = value; }
}
private List<TipBoxModel> _tipbox = new List<TipBoxModel>();
/// <summary>
/// 料架上第一层的 Tip工装集合
/// </summary>
public List<TipBoxModel> Rack_TipBoxList
{
get { return _tipbox; }
set { _tipbox = value; }
}
private List<SourceLiquidBottleBoxModel> sourceLiquidBottleBoxs = new List<SourceLiquidBottleBoxModel>();
/// <summary>
/// 料架上第一层的 原液工装集合
/// </summary>
public List<SourceLiquidBottleBoxModel> Rack_SourceLiquidBottlesBoxList
{
get { return sourceLiquidBottleBoxs; }
set { sourceLiquidBottleBoxs = value; }
}
#endregion
private string lastOpenProject = string.Empty;
/// <summary>
/// 关闭软件最后一次打开的项目
/// </summary>
public string LastOpenProject
{
get => lastOpenProject;
set => SetProperty(ref lastOpenProject, value);
}
private string nowOpenProjectFile;
public string NowOpenProjectFile
{
get => nowOpenProjectFile;
set => SetProperty(ref nowOpenProjectFile, value);
}
private string Dose_ct = string.Empty;
[XmlIgnore]
public string Dose_CT
{
get => Dose_ct;
set => SetProperty(ref Dose_ct, value);
}
private string react_ct = string.Empty;
[XmlIgnore]
public string React_CT
{
get => react_ct;
set => SetProperty(ref react_ct, value);
}
public ProjectProperty()
{
}
}
}

View File

@ -0,0 +1,102 @@
using MegaRobo.C00225155.Entities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Animation;
using System.Xml.Serialization;
namespace Common
{
public class StationProperty
{
private CancellationTokenSource cancellation = new CancellationTokenSource();
/// <summary>
/// 工站线程取消标志
/// </summary>
public CancellationTokenSource cancellationToken
{
get { return cancellation; }
set { cancellation = value; }
}
/// <summary>
/// 取消 暂停标志
/// </summary>
public ManualResetEvent ManualResetEvent { get; set; } = new ManualResetEvent(true);
//private Task stationtask;
//public Task stationTask
//{
// get { return stationtask; }
// set { stationtask = value; }
//}
private int stationnum;
/// <summary>
/// 工站编号
/// </summary>
public int StationNum
{
get { return stationnum; }
set { stationnum = value; }
}
private string stationname = string.Empty;
public string StationName
{
get { return stationname; }
set { stationname = value; }
}
/// <summary>
/// 是否启动按钮触发
/// </summary>
public bool bStartButtonTrigger = false;
/// <summary>
/// 是否停止按钮触发
/// </summary>
public bool bStopButtonTrigger = false;
/// <summary>
/// 是否复位按钮触发
/// </summary>
public bool bResetButtonTrigger = false;
/// <summary>
/// 工站是否运行结束
/// </summary>
public bool bRunFinish = false;
/// <summary>
/// 工站是否已经复位,只有复位过才能启动
/// </summary>
public bool bHaveReset = false;
/// <summary>
/// 工站计时CT
/// <summary>
public Stopwatch CTWatch = new Stopwatch();
public DeviceStatus deviceStatus = DeviceStatus.Idel;
/// <summary>
/// 阻塞信号集合
/// </summary>
public ConcurrentDictionary<string,ObstructFlag> ObstructFlagList { get; set; } = new ConcurrentDictionary<string, ObstructFlag>();
//private CancellationTokenSource cancellationUpDn = new CancellationTokenSource();
///// <summary>
///// 上下工装线程取消标志(不控制工站线程)
///// </summary>
//public CancellationTokenSource cancellationTokenUpDn
//{
// get { return cancellationUpDn; }
// set { cancellationUpDn = value; }
//}
}
}

View File

@ -0,0 +1,73 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Models
{
///// <summary>
///// 取样接口
///// </summary>
//public interface ISamplingModel
//{
// double Height { get; set; }
//}
///// <summary>
///// 穿刺取样模型
///// </summary>
//[Serializable]
//public class PunctureSamplingModel : ObservableObject, ISamplingModel
//{
// private double height;
// /// <summary>
// /// 取样高度
// /// </summary>
// public double Height
// {
// get { return height; }
// set { SetProperty(ref height, value); }
// }
//}
///// <summary>
///// 开盖取样模型
///// </summary>
//[Serializable]
//public class OpenLidSamplingModel : ObservableObject, ISamplingModel
//{
// private double intervalTime;
// /// <summary>
// /// 取样间隔时间单位min
// /// </summary>
// public double IntervalTime
// {
// get { return intervalTime; }
// set { SetProperty(ref intervalTime, value); }
// }
// private double height;
// /// <summary>
// /// 取样高度
// /// </summary>
// public double Height
// {
// get { return height; }
// set { SetProperty(ref height, value); }
// }
// private int count;
// /// <summary>
// /// 取样次数
// /// </summary>
// public int Count
// {
// get { return count; }
// set { SetProperty(ref count, value); }
// }
//}
}

View File

@ -0,0 +1,49 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Models
{
[Serializable]
public class SampleBottleTemperatureModel : ObservableObject
{
private double rate = 0;
/// <summary>
/// 震荡频率
/// </summary>
public double Speed
{
get { return rate; }
set => SetProperty(ref rate, value);
}
private double time = 0;
/// <summary>
/// 时间(设备到目标温度后,再开始算时间),单位是min
/// </summary>
public double Time
{
get => time;
set
{
SetProperty(ref time, value);
}
}
private double targetTemperature = 150;
/// <summary>
/// 目标温度
/// </summary>
public double TargetTemperature
{
get => targetTemperature;
set
{
SetProperty(ref targetTemperature, value);
}
}
}
}

View File

@ -0,0 +1,354 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace Common.Models
{
public enum FunctionTypeEnum
{
[Description("添加液体原料")]
,
[Description("添加固体原料")]
,
[Description("震荡摇匀")]
,
}
[Serializable]
public class MaterialDoseFunctionFlow : ObservableObject
{
private bool bisdosefinish = false;
/// <summary>
/// 是否配置完成
/// </summary>
public bool IsDoseFinish
{
get { return bisdosefinish; }
set { SetProperty(ref bisdosefinish, value); }
}
private FunctionTypeEnum functionType = FunctionTypeEnum.;
public FunctionTypeEnum FunctionFlowType
{
get => functionType;
set
{
SetProperty(ref functionType, value);
}
}
private string useMaterialName = string.Empty;
/// <summary>
/// 使用的试剂名称
/// </summary>
public string UseMaterialName
{
get { return useMaterialName; }
set
{
SetProperty(ref useMaterialName, value);
}
}
private double targetVolume = 0;
/// <summary>
/// 目标体积μl/目标重量(mg)
/// </summary>
public double TargetVolume
{
get { return targetVolume; }
set
{
SetProperty(ref targetVolume, value);
}
}
private double actualVolume;
/// <summary>
/// 实际加入体积/重量
/// </summary>
public double ActualVolume
{
get { return actualVolume; }
set
{
SetProperty(ref actualVolume, value);
}
}
//private double liquidprecision = 1;
///// <summary>
///// 液体加液最大精度,单位g
///// </summary>
//public double LiquidPrecision
//{
// get { return liquidprecision; }
// set
// {
// SetProperty(ref liquidprecision, value);
// }
//}
}
//[Serializable]
//public class SampleBottlePowderFunctionFlow : ObservableObject
//{
// private FunctionTypeEnum functionType = FunctionTypeEnum.加固体原料;
// public FunctionTypeEnum FunctionFlowType
// {
// get => functionType;
// set
// {
// SetProperty(ref functionType, value);
// }
// }
// private string useSourcePowderName = string.Empty;
// /// <summary>
// /// 使用的粉末试剂名称
// /// </summary>
// public string UsePowderName
// {
// get { return useSourcePowderName; }
// set
// {
// SetProperty(ref useSourcePowderName, value);
// }
// }
// private double targetPowderAmount;
// /// <summary>
// /// 粉末目标加入量单位g
// /// </summary>
// public double TargetPowderAmount
// {
// get { return targetPowderAmount; }
// set
// {
// if (targetPowderAmount != value)
// {
// targetPowderAmount = value;
// PowderPrecision = PrecisionRuleHelper.GetPowderPrecision(value); // 根据目标量获取精度g
// OnPropertyChanged(nameof(TargetPowderAmount));
// OnPropertyChanged(nameof(PowderPrecision));
// }
// }
// }
// private double actualPowderAmount;
// /// <summary>
// /// 粉末实际加入量,单位g
// /// </summary>
// public double ActualPowderAmount
// {
// get { return actualPowderAmount; }
// set
// {
// SetProperty(ref actualPowderAmount, value);
// }
// }
// private double powderprecision = 1;
// /// <summary>
// /// 粉末加量最大精度,单位g
// /// </summary>
// public double PowderPrecision
// {
// get { return powderprecision; }
// set
// {
// SetProperty(ref powderprecision, value);
// }
// }
//}
public enum ReactFunctionTypeEnum
{
[Description("取样")]
,
[Description("补液")]
,
}
/// <summary>
/// 取样操作类
/// </summary>
[Serializable]
public class ReactSampleFunctionFlow : ObservableObject
{
private double time;
/// <summary>
/// 操作时间
/// </summary>
public double OperateTime
{
get { return time; }
set { time = value; }
}
private double targetVolume = 0;
/// <summary>
/// 取样体积
/// </summary>
public double TargetAddVolume
{
get { return targetVolume; }
set
{
SetProperty(ref targetVolume, value);
}
}
private bool bAddLiquid = false;
/// <summary>
/// 取样后是否紧接着加样
/// </summary>
public bool bAfterAddLiquid
{
get { return bAddLiquid; }
set { SetProperty(ref bAddLiquid, value); }
}
private ObservableCollection<AddLiquidFunctionFlow> addLiquidList = new ObservableCollection<AddLiquidFunctionFlow>();
/// <summary>
/// 加液集合
/// </summary>
public ObservableCollection<AddLiquidFunctionFlow> AddLiquidList
{
get { return addLiquidList; }
set { SetProperty(ref addLiquidList, value); }
}
private bool _isDuilte = false;
/// <summary>
/// 是否稀释,默认都会稀释
/// </summary>
public bool IsDulite
{
get => _isDuilte;
set => SetProperty(ref _isDuilte, value);
}
private ObservableCollection<AddDiluentFunction> diluentList = new ObservableCollection<AddDiluentFunction>();
/// <summary>
/// 稀释剂集合
/// </summary>
public ObservableCollection<AddDiluentFunction> DiluentFunctionFlowList
{
get { return diluentList; }
set { SetProperty(ref diluentList, value); }
}
}
[Serializable]
public class AddLiquidFunctionFlow : ObservableObject
{
private double time;
/// <summary>
/// 操作时间
/// </summary>
public double OperateTime
{
get { return time; }
set { time = value; }
}
private string liquidName = string.Empty;
/// <summary>
/// 液体名称
/// </summary>
public string LiquidName
{
get { return liquidName; }
set { SetProperty(ref liquidName, value); }
}
private double targetVolume;
/// <summary>
///目标加入体积
/// </summary>
public double TargetAddVolume
{
get { return targetVolume; }
set
{
SetProperty(ref targetVolume, value);
}
}
private double actualVolume;
/// <summary>
/// 实际加入体积
/// </summary>
public double ActualVolume
{
get { return actualVolume; }
set
{
SetProperty(ref actualVolume, value);
}
}
}
/// <summary>
/// 添加稀释剂
/// </summary>
[Serializable]
public class AddDiluentFunction : ObservableObject
{
private string diluentName = string.Empty;
/// <summary>
/// 稀释剂名称
/// </summary>
public string DiluentName
{
get { return diluentName; }
set { SetProperty(ref diluentName, value); }
}
private double targetDiluentVolume = 0.00;
/// <summary>
///稀释剂目标加入体积
/// </summary>
public double TargetDiluentVolume
{
get { return targetDiluentVolume; }
set
{
SetProperty(ref targetDiluentVolume, value);
}
}
private double actualDiluentVolume = 0.00;
/// <summary>
/// 稀释剂实际加入体积
/// </summary>
public double ActualDiluentVolume
{
get { return actualDiluentVolume; }
set
{
SetProperty(ref actualDiluentVolume, value);
}
}
private double diluentfactor = 1.00;
/// <summary>
/// 稀释倍数,自动计算
/// </summary>
public double DiluentFactor
{
get { return diluentfactor; }
set { SetProperty(ref diluentfactor, value); }
}
}
}

View File

@ -0,0 +1,68 @@
using Common.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public static class EnumHelper
{
// 获取枚举所有值(可在 XAML 中直接引用)
public static IEnumerable<FunctionTypeEnum> FunctionTypes =>
Enum.GetValues(typeof(FunctionTypeEnum)).Cast<FunctionTypeEnum>();
//反应功能枚举
public static IEnumerable<ReactFunctionTypeEnum> ReactFunctionTypes =>
Enum.GetValues(typeof(ReactFunctionTypeEnum)).Cast<ReactFunctionTypeEnum>();
//SampleBottleTypeEnum
public static IEnumerable<SampleBottleTypeEnum> SampleBottleTypes =>
Enum.GetValues(typeof(SampleBottleTypeEnum)).Cast<SampleBottleTypeEnum>();
public static IEnumerable<PowderBottleTypeEnum> PowderBottleTypes =>
Enum.GetValues(typeof(PowderBottleTypeEnum)).Cast<PowderBottleTypeEnum>();
/// <summary>
/// 高温反应 低温反应
/// </summary>
public static IEnumerable<SampleBottleReactTemperatureEnum> ReactTemperatureEnums =>
Enum.GetValues(typeof(SampleBottleReactTemperatureEnum)).Cast<SampleBottleReactTemperatureEnum>();
/// <summary>
/// 操作类型 取样/加液
/// </summary>
public static IEnumerable<ReactOperationEnum> ReactOperateTypeEnums =>
Enum.GetValues(typeof(ReactOperationEnum)).Cast<ReactOperationEnum>();
/// <summary>
/// 反应取样方式枚举 穿刺取样/开盖取样
/// </summary>
public static IEnumerable<SampleMethod> ReactSampleTypeEnums =>
Enum.GetValues(typeof(SampleMethod)).Cast<SampleMethod>();
/// <summary>
/// 加液方式 穿刺加液/开盖加液
/// </summary>
public static IEnumerable<AddLiquidMethod> AddLiquidTypeEnums =>
Enum.GetValues(typeof(AddLiquidMethod)).Cast<AddLiquidMethod>();
/// <summary>
/// 稀释方式
/// </summary>
public static IEnumerable<DiluteMethod> DiluteTypeEnums =>
Enum.GetValues(typeof(DiluteMethod)).Cast<DiluteMethod>();
public static IEnumerable<TipTypeEnum> TipHeadTypeEnums =>
Enum.GetValues(typeof(TipTypeEnum)).Cast<TipTypeEnum>();
public static IEnumerable<BoxTypeEnum> BoxTypeEnums =>
Enum.GetValues(typeof(BoxTypeEnum)).Cast<BoxTypeEnum>();
public static IEnumerable<BoxTypeEnum> BoxTypeEnumsSampleSource = new ObservableCollection<BoxTypeEnum>() { BoxTypeEnum._40mL原液工装, BoxTypeEnum._12mL样品工装, BoxTypeEnum._5mL样品工装 };
}
}

View File

@ -0,0 +1,72 @@
using Common.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Common.HTTPModels
{
public class ControlCmd
{
public string command { get; set; }
[JsonPropertyName("params")]
[JsonProperty("params")]
public object Params { get; set; }
}
public class UnLoadModel
{
/// <summary>
/// 是否继续上料
/// </summary>
public bool isContinueLoad { get; set; }
public bool isBSRequest { get; set; }
public List<SourcePowderBottleBoxModel> sourcePowderBottleBoxModel { get; set; }
}
public class UploadBoxModel
{
/// <summary>
/// 工装类型
/// </summary>
public BoxTypeEnum fixtureType { get; set; }
/// <summary>
/// 盒子码
/// </summary>
public string boxSNCode { get; set; }
/// <summary>
/// 位置1~7
/// </summary>
public int boxId_inDoseUpload { get; set; }
/// <summary>
/// 剩余tip个数如果fixtureType == 8~10需要
/// </summary>
public int RemainCount { get; set; }
/// <summary>
/// 样品瓶
/// </summary>
public List<SampleBottleModel> sampleBottles { get; set; }
/// <summary>
/// 原液瓶
/// </summary>
public List<SourceLiquidBottleModel> sourceLiquidBottles { get; set; }
/// <summary>
/// 粉末
/// </summary>
public List<SourcePowderBottleModel> sourcePowderBottles { get; set; }
}
}

View File

@ -0,0 +1,157 @@
using Common.Models;
using MegaRobo.C00225155.Entities.FromWeb;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.HTTPModels
{
public class ToBsUnLoadData
{
[JsonProperty("sampleSeqs")]
public List<ToBsSampleSeq> SampleSeqs { get; set; }
[JsonProperty("config")]
public ToBsConfig Config { get; set; }
[JsonProperty("needFixture")]
public List<ToBsFixtureInfo> NeedFixture { get; set; }
}
#region SampleSeq类
public class ToBsSampleSeq
{
/// <summary>
/// 手套箱是A1
/// </summary>
[JsonProperty("area")]
public string Area { get; set; }
/// <summary>
/// 载具位置1~7
/// </summary>
[JsonProperty("carrier")]
public string Carrier { get; set; }
/// <summary>
/// 样本瓶名称
/// </summary>
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("order")]
public object Order { get; set; }
/// <summary>
/// 样品编号
/// </summary>
[JsonProperty("originSeqIds")]
public List<long> OriginSeqIds { get; set; }
/// <summary>
/// 在载具中的位置
/// </summary>
[JsonProperty("pos")]
public string Pos { get; set; }
[JsonProperty("sampleConfig")]
public ToBsSampleConfig SampleConfig { get; set; }
/// <summary>
/// _5mL = 1,_12mL = 2,其它 = 0
/// </summary>
[JsonProperty("sampleType")]
public string SampleType { get; set; }
}
public class ToBsSampleConfig
{
[JsonProperty("materialDoseFunctionFlow")]
public List<ToBsMaterialDoseFunctionFlow> MaterialDoseFunctionFlow { get; set; }
/// <summary>
/// box 二维码匹配下面的boxinfo
/// </summary>
[JsonProperty("snCode")]
public string SnCode { get; set; }
}
public class ToBsMaterialDoseFunctionFlow
{
/// <summary>
/// 0液1固
/// </summary>
[JsonProperty("functionFlowType")]
public int FunctionFlowType { get; set; }
[JsonProperty("realVolumn")]
public double RealVolumn { get; set; }
/// <summary>
/// 实际添加量
/// </summary>
[JsonProperty("targetVolumn")]
public double TargetVolumn { get; set; }
/// <summary>
/// 添加物名称
/// </summary>
[JsonProperty("userMaterialName")]
public string UserMaterialName { get; set; }
}
#endregion
#region config类
public class ToBsConfig
{
[JsonProperty("boxInfo")]
public List<ToBsBoxInfo> BoxInfo { get; set; }
}
public class ToBsBoxInfo
{
[JsonProperty("detail")]
public List<Detail> Detail { get; set; }
[JsonProperty("fixtureType")]
public int FixtureType { get; set; }
[JsonProperty("posInUpload")]
public int PosInUpload { get; set; }
[JsonProperty("remainCount")]
public int RemainCount { get; set; }
[JsonProperty("snCode")]
public string SnCode { get; set; }
}
#endregion
#region NeedFixture类
public class ToBsFixtureInfo
{
/// <summary>
/// 放到转移位置1~7
/// </summary>
[JsonProperty("boxIdInDoseUpload")]
public int BoxIdInDoseUpload { get; set; }
/// <summary>
/// 治具类型
/// </summary>
[JsonProperty("fixtureType")]
public int FixtureType { get; set; }
/// <summary>
/// 治具二维码
/// </summary>
[JsonProperty("snCode")]
public string SnCode { get; set; }
}
#endregion
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class IniHelper
{
[DllImport("kernel32")]
public static extern bool WritePrivateProfileString(string lpapplication, string lpkeyname, string lpstring, string lpfilename);
[DllImport("kernel32")]
public static extern int GetPrivateProfileString(string lpszSection, string lpszKey, string lpszDefault, System.Text.StringBuilder lpReturnedString, int cchReturnBuffer, string lpsFile);
public static string IniRead(string _filePath, string _section, string _key, string _default)
{
System.Text.StringBuilder _builder = new System.Text.StringBuilder(1024);
int len = GetPrivateProfileString(_section, _key, _default, _builder, 1024, _filePath);
string str = Convert.ToString(_builder).Trim();
int num = str.IndexOf(Convert.ToChar(0));
if (num >= 0) str = str.Substring(0, num - 1);
return str;
}
public static bool IniWrite(string _filePath, string _section, string _key, string _value)
{
return WritePrivateProfileString(_section, _key, _value, _filePath);
}
}
public class EnumItem<TEnum> where TEnum : struct, Enum
{
public TEnum Value { get; set; } // 枚举值如MA
public string DisplayName { get; set; } // 显示文本(如“电容式”)
}
public class EnumHelperr
{
public static string GetDescription(Enum value)
{
Type type = value.GetType();
MemberInfo[] memberInfo = type.GetMember(value.ToString());
if (memberInfo.Length > 0)
{
object[] attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return ((DescriptionAttribute)attributes[0]).Description;
}
}
return value.ToString();
}
public static List<EnumItem<TEnum>> GetEnumDescriptionList<TEnum>() where TEnum : struct, Enum
{
var list =new List<EnumItem<TEnum>>();
foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum)))
{
list.Add(new EnumItem<TEnum>
{
Value = enumValue, // 存储原始枚举值
DisplayName = GetDescription(enumValue) // 存储显示文本
});
}
return list;
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public static class PrecisionRuleHelper
{
// 液体加量对应的精度规则key为加量区间下限包含value为精度计算方式
public static Dictionary<double, Func<double, double>> LiquidPrecisionRules = new Dictionary<double, Func<double, double>>
{
{1, (target) => target * 0.015}, // 1~5ml 对应 ±1.5%,这里简单示例返回目标值*0.015作为精度值(实际可根据需求调整存储和计算)
{0.2, (target) => target * 0.02}, // 0.2~1ml 对应 ±1%-2%这里简单取2%示例
{0.02, (target) => target * 0.02} // 0.02~0.2ml 对应 ±1%-2%这里简单取2%示例
};
// 粉末加量对应的精度规则key为加量区间下限包含value为精度计算方式
public static Dictionary<double, Func<double, double>> PowderPrecisionRules = new Dictionary<double, Func<double, double>>
{
{5, (target) => target * 0.01}, // 5~10g 对应 ±1%
{1, (target) => target * 0.015}, // 1~5g 对应 ±1.5%
{0.1, (target) => target * 0.02}, // 0.1~1g 对应 ±1%-2%这里简单取2%示例
{0.05, (target) => target * 0.02}, // 0.05~0.1g 对应不低于±2%
{0.005, (target) => 0.00025} // 0.005~0.05g 对应±0.25mg
};
/// <summary>
/// 获取液体加量对应的精度值
/// </summary>
/// <param name="targetAmount">液体目标加量</param>
/// <returns>计算后的精度值</returns>
public static double GetLiquidPrecision(double targetAmount)
{
foreach (var rule in LiquidPrecisionRules)
{
if (targetAmount >= rule.Key)
{
return rule.Value(targetAmount);
}
}
return 1; // 默认精度,可根据实际需求调整
}
/// <summary>
/// 获取粉末加量对应的精度值
/// </summary>
/// <param name="targetAmount">粉末目标加量</param>
/// <returns>计算后的精度值</returns>
public static double GetPowderPrecision(double targetAmount)
{
foreach (var rule in PowderPrecisionRules)
{
if (targetAmount >= rule.Key)
{
return rule.Value(targetAmount);
}
}
return 1; // 默认精度,可根据实际需求调整
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class TipMessageEventArgs:EventArgs
{
// 提示信息
public string Message { get; set; }
// 操作ID用于区分不同的操作
public bool IsCompleted { get; set; }
public TipMessageEventArgs(string message, bool isCompleted)
{
Message = message;
IsCompleted = isCompleted;
}
public delegate void OperationCompletedHandler(object sender, TipMessageEventArgs e);
// 操作进度更新委托
public delegate void OperationProgressHandler(int progress, string message);
}
}

View File

@ -0,0 +1,16 @@
<Window x:Class="Common.TipMessageWindow"
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:Common"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen" Topmost="True" WindowStyle="None"
Height="250" Width="480">
<Grid>
<StackPanel Margin="20,10">
<TextBlock x:Name="MessageTextBlock" HorizontalAlignment="Center" FontSize="16" Height="80" Width="400" TextWrapping="Wrap" TextAlignment="Left" Margin="0,40,0,20" />
<ProgressBar IsIndeterminate="True" Height="30" Width="320" Margin="20"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,97 @@
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 Common
{
/// <summary>
/// TipMessageWindow.xaml 的交互逻辑
/// </summary>
public partial class TipMessageWindow : Window
{
public TipMessageWindow(string message)
{
InitializeComponent();
MessageTextBlock.Text = message;
}
}
public static class DialogManager
{
// 事件:操作完成通知
public static event Action OperationCompleted;
// 显示提示窗口并等待操作完成
public static async Task ShowDialogAsync(Window owner, string message, Func<Task> operation)
{
var dialog = new TipMessageWindow(message);
dialog.Owner = owner;
// 步骤1先订阅事件确保操作触发事件前已订阅
var closeTask = WaitForDialogCloseAsync(dialog);
// 步骤2启动耗时操作此时事件已订阅
var task = Task.Run(async () =>
{
try
{
await operation?.Invoke();
}
finally
{
// 操作完成(无论成功/失败)都触发关闭事件
OperationCompleted?.Invoke();
}
});
// 步骤3显示弹窗
dialog.Show();
// 步骤4等待弹窗关闭 + 操作完成
await closeTask;
await task;
}
private static Task WaitForDialogCloseAsync(Window dialog)
{
var tcs = new TaskCompletionSource<bool>();
// 定义事件处理方法(方便后续取消订阅)
Action operationCompletedHandler = null;
// 事件触发时关闭弹窗
operationCompletedHandler = () =>
{
// 必须在UI线程操作弹窗
dialog.Dispatcher.Invoke(() =>
{
if (dialog.IsVisible)
dialog.Close();
});
// 取消订阅(避免重复触发)
OperationCompleted -= operationCompletedHandler;
};
// 订阅事件
OperationCompleted += operationCompletedHandler;
// 弹窗关闭时完成Task + 取消订阅
dialog.Closed += (s, e) =>
{
OperationCompleted -= operationCompletedHandler;
tcs.SetResult(true);
};
return tcs.Task;
}
}
}

View File

@ -0,0 +1,51 @@
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
/// <summary>
/// 配置站启动按钮状态更新
/// </summary>
public class SingleBoolMessage : ValueChangedMessage<bool>
{
public SingleBoolMessage(bool bfinish) : base(bfinish)
{
}
}
/// <summary>
/// 检测站启动按钮状态更新
/// </summary>
public class SingleBoolMessage1 : ValueChangedMessage<bool>
{
public SingleBoolMessage1(bool bfinish) : base(bfinish)
{
}
}
/// <summary>
/// 配方流程导航栏选项是否可以点击
/// </summary>
public class SingleBoolPeifangMessage : ValueChangedMessage<bool>
{
public SingleBoolPeifangMessage(bool bfinish) : base(bfinish)
{
}
}
/// <summary>
/// 更新拉曼参数
/// </summary>
public class UpdateRamanParamMessage : ValueChangedMessage<(int laservalue, int expvalue, int testCount, string fileName)>
{
public UpdateRamanParamMessage((int laserpowderrate, int expvalue, int testCount, string fileName) value) : base(value) { }
}
}

View File

@ -0,0 +1,52 @@
using System.Collections.Generic;
using MegaRobo.Entities;
using MegaRobo.C00225155.Entities;
using MegaRobo.Entities.ViewTT;
using MegaRobo.C00225155.Entities.Enums;
using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;
using Common;
namespace MegaRobo.C00225155.AppServer;
public class AppConfigService
{
public AppConfigService()
{
ReadConfig();
}
public ProjectInfo ProjectInfo { get; set; }
public IList<Setting> SettingList { get; set; }
//public IList<WebApiUrl> WebApiUrlList { get; set; }
public readonly string ApiName = "C00225155";
public readonly string globalOrderId = "0";
public List<string> BlockKeywords { get; set; } = new List<string>();
/// <summary>
/// 配置站、检测站公用的访问数据库的锁
/// </summary>
public readonly SemaphoreSlim databaseSemaphoreSlim = new SemaphoreSlim(1, 1);
///// <summary>
///// 工站属性列表
///// </summary>
//public List<StationProperty> StationPropList = new List<StationProperty>();
public IDictionary<ViewNames, AuthLevel> ViewNameAuthLevelsDic = new Dictionary<ViewNames, AuthLevel>();
private void ReadConfig()
{
ViewNameAuthLevelsDic.Clear();
ViewNameAuthLevelsDic.Add(ViewNames.HomeView, AuthLevel.None);
ViewNameAuthLevelsDic.Add(ViewNames.SettingView, AuthLevel.Operator);
ViewNameAuthLevelsDic.Add(ViewNames.CommunicationDebugView, AuthLevel.None);
ViewNameAuthLevelsDic.Add(ViewNames.FlowConfigView, AuthLevel.None);
ViewNameAuthLevelsDic.Add(ViewNames.AdditivePipetteConfigView, AuthLevel.None);
ViewNameAuthLevelsDic.Add(ViewNames.LoggerView, AuthLevel.None);
ViewNameAuthLevelsDic.Add(ViewNames.UserView, AuthLevel.Instructor);
}
}

View File

@ -0,0 +1,357 @@
using Common;
using Common.Models;
using CommunityToolkit.Mvvm.Messaging;
using MegaRobo.C00225155.DataAccess;
using MegaRobo.C00225155.Entities;
using MegaRobo.C00225155.Entities.Entity_DB;
using MegaRobo.C00225155.Entities.ToWeb;
using MegaRobo.Contract;
using MegaRobo.ControlDevices;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.ControlDevices.Models;
using MegaRobo.Entities;
using MegaRobo.Logger;
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.HamiltonDevices;
using MegaRobo.Utility.Files;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace MegaRobo.C00225155.AppServer.ExecuteWorks;
public abstract class StationServiceBase : ModbusWorkServiceBase
{
#region
protected WorkService workService { get; set; }
protected AppConfigService appConfigService;
/// <summary>
/// PLC通讯的ModbusClient
/// </summary>
protected IModbusClientExtend ModbusClient;
/// <summary>
/// PLC 名称
/// </summary>
protected DeviceNames PlcName;
protected IDataAccessService dataAccessService;
protected IDictionary<string, string> PipetteErrorDic = new Dictionary<string, string>();
//protected StationProperty StationProp = new StationProperty();
#endregion
#region Field\Property
/// <summary>
/// 从数据库中加载的所有PLC报警点位
/// </summary>
protected IList<PlcPoint> WarnPointList { get; set; }
/// <summary>
/// 从CSV文件中加载的所有报警点位、报警信息
/// </summary>
protected List<PLCErrorPoint> PLCErrorInfoList { get; set; } = new List<PLCErrorPoint>();
/// <summary>
/// 当前报警信息,触发事件
/// </summary>
protected List<PLCErrorPoint> PLCErrorLists { get; set; } = new List<PLCErrorPoint>();
/// <summary>
///扫描PLC 错误信息集合事件
/// </summary>
public event EventHandler<List<PLCErrorPoint>> OnScanPlcErrorListEvent;
protected bool Runing = false;
protected string stationName = string.Empty;
#endregion
protected StationServiceBase(ICoreService coreService, WorkService workService, DeviceNames plcName, string stationName)
{
this.PlcName = plcName;
this.stationName = stationName;
this.CoreService = coreService;
this.workService = workService;// 关联上级WorkService便于跨工站交互
appConfigService = this.CoreService.GetServiceInstance<AppConfigService>();// 配置服务
dataAccessService = this.CoreService.GetServiceInstance<IDataAccessService>();// 数据访问服务
}
protected override void LoadServices()
{
this.ModbusClient = this.CoreService.GetServiceInstance<IModbusClientExtend>(this.PlcName.ToString());
if (this.ModbusClient is not null)
{
this.ModbusClient.ScannedEvent += this.ModbusClientBaseOnScannedEvent;
this.ModbusClient.PulseTickAction += this.OnPulseTick;
}
}
protected override void LoadDatas()
{
base.LoadDatas();
RegisterPipetteErrorCode();
}
protected override async void RegisterPoints()
{
//转移至 StartupService 统一注册
}
protected void RegisterPipetteErrorCode()
{
PipetteErrorDic = new Dictionary<string, string>()
{
{ "00", "执行成功" },
{ "20", "硬件问题请联系Hamilton"},
{ "30", "未知命令,请参考命令表"},
{ "31", "未知参数,请参考命令表"},
{ "32", "发送的参数值超出预定范围,请参考命令表"},
{ "35", "电源电压超出运行范围,请确保电源电压是48V+-5%"},
{ "36", "关闭紧急停止"},
{ "38", "空液体类型"},
{ "39", "液体类型写保护"},
{ "40", "一次只能发送一个命令"},
{ "50", "Z轴初始化失败,硬件问题请联系Hamilton"},
{ "51", "请初始化泵驱动"},
{ "52", "泵驱动初始化失败,硬件问题请联系Hamilton"},
{ "53", "tip 头容量不够无法吸入ai 值得液体改变ai值或更改tip 头"},
{ "54", "达到泵的最大值"},
{ "55", "吸液体积校验失败吸液量不满足当前Tip头的最小值"},
{ "56", "选择的液体类型要求用导电tip 头请确认液体类型和tip 头类型编号一致"},
{ "57", "确定液体类型一致和tip 头类型编号"},
{ "60", "ZI初始化失败硬件未工作请联系Hamilton"},
{ "61", "Z轴驱动初始化失败"},
{ "62", "Z 轴驱动移动错误 硬件未工作请联系Hamilton"},
{ "63", "移动碰触到试管底部"},
{ "64", "Z 轴驱动达不到gy(要移动到的位置) 值,请检查参数"},
{ "65", "Z 轴驱动达不到te(转移高度)值,请检查参数"},
{ "66", "Z 轴驱动达不到ce(容器底部高度) 值,请检查参数"},
{ "67", "Z 轴驱动达不到cf(液面的高度) 值,请检查参数"},
{ "68", "Z 轴驱动达不到zp(开始探测液面的位置) 值,请检查参数"},
{ "69", "Z 轴驱动达不到tr(丢弃Tip头位置) 值,请检查参数"},
{ "70", "在zp 和ce 两个值之间未检测到液面,请检查参数设置"},
{ "71", "液体不足"},
{ "72", "压力传感器自动校准失效,硬件未工作请联系Hamilton"},
{ "74", "提前检测到液面"},
{ "75", "未取到tip 头"},
{ "76", "tip 头已经取到"},
{ "80", "吸液过程中检测到堵塞"},
{ "81", "吸液过程中检测到吸空"},
{ "82", "吸液过程中检测到气泡"},
{ "83", "分配过程中检测到堵塞"},
{ "84", "分配过程中检测到气泡"},
{ "85", "未与数字电平通信上硬件问题请联系Hamilton"},
};
}
protected override void LoadPlcWarnInfoSetting()
{
base.LoadPlcWarnInfoSetting();
this.PLCErrorInfoList.Clear();
WarnPointList = new List<PlcPoint>();
string filePath = string.Empty;
if (this.PlcName == DeviceNames.Plc1)//投料站
{
filePath = "DataBox\\PlcWarnSetting_Dose.csv";
}
else if (this.PlcName.Equals(DeviceNames.Plc2)) //反应站
{
filePath = "DataBox\\PlcWarnSetting_React.csv";
}
string[] lines = File.ReadAllLines(filePath, System.Text.Encoding.GetEncoding("utf-8"));
foreach (string line in lines)
{
string key = string.Empty;
string[] columns = line.Split(',');
if (columns.Length != 2)
{
continue;
}
key = columns[0].Replace("MX", "");
string value = columns[1];
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value)) continue;
if (!double.TryParse(key, out double _point)) continue;
PLCErrorPoint point = new PLCErrorPoint() { ErrorAddress = key, ErrorInfo = value };
this.PLCErrorInfoList.Add(point);
}
var alladdress = PLCErrorInfoList.Select(x => x.ErrorAddress.Split(".")[0]).Distinct().ToList();
var client = this.CoreService.GetServiceInstance<IModbusClientExtend>("Plc1");
foreach (var addr in alladdress)
{
client.Register<System.Drawing.Point>(ushort.Parse(addr), $"报警点位-{addr}", FunType.ReadHoldingRegisters);
PlcPoint plcPoint = new PlcPoint()
{
Address = ushort.Parse(addr), Descr = $"报警点位-{addr}", FunType = FunType.ReadHoldingRegisters
};
WarnPointList.Add(plcPoint);
}
//this.WarnPointList =
//this.WarnPointList = this.dataAccessService.LoadEntitiesAsync<PlcPoint>(x => x.Name == this.PlcName.ToString() && x.Level == 2).Result;
this.Runing = true;
}
protected string GetMethodName([CallerMemberName] string memberName = "")
{
return memberName;
}
//protected virtual async Task InitPreAsync()
//{
// await Task.Delay(0);
//}
#region PLC读取/
#endregion
private ushort _lastTickValue = 1; //先缓存, 尽量减少通讯次数
protected override void OnPulseTick()
{
var currValue = (ushort)(DateTime.Now.Second % 2); //间隔两秒给一次心跳
if (currValue == 0)
{
//this._lastTickValue = currValue;
this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_MaterialDose.W_PCHeartPluse[(ushort)this.PlcName], 1);
}
}
#region ModBus
protected async void LoadPlcAlarmInfoSetting()
{
//注册提示点位
// this.WarnPointList = await this.DataAccessService.LoadEntitiesAsync<PlcPoint>(x =>
// x.Name == this.PlcName.ToString() && x.Level == 3);
}
//protected IList<PlcPoint> ScanPlcAlarmInfoList(int? takeCount = null)
//{
// return new List<PlcPoint>();
//}
private DateTime lastWarnDateTime = DateTime.Now;
//public Dictionary<ushort, ushort> WarnDictionary = new Dictionary<ushort, ushort>();
protected virtual void ModbusClientBaseOnScannedEvent(object sender, EventArgs e)
{
if (!this.Runing)
return;
DateTime now = DateTime.Now;
if ((now - this.lastWarnDateTime).Seconds >= 5)
{
this.lastWarnDateTime = now;
ScanPLCWarnList();
}
}
public override IList<PlcPoint> ScanPlcWarnInfoList(int? takeCount = null)
{
IList<PlcPoint> warnPointList = new List<PlcPoint>();
try
{
foreach (var plcPoint in this.WarnPointList)
{
//这里的错误报警信息都是1个字2个字节16位
var pointInfo = this.ModbusClient.ReadScannedValue<ushort>(plcPoint.Address);
if (pointInfo.Value != 0)
{
//if (!this.WarnDictionary.ContainsKey(pointInfo.Address) || this.WarnDictionary[pointInfo.Address] != pointInfo.Value)
//{
// this.Logger.LogError($"{this.stationName}异常点位[{pointInfo.Address}]值[{pointInfo.Value}]");
// this.WarnDictionary[pointInfo.Address] = pointInfo.Value;
//}
//pointInfo.LastValue = pointInfo.Value.ToString();
warnPointList.Add(pointInfo);
}
// else
// {
// //复位直接清空 不处理
// if(this.WranDictionary.ContainsKey(pointInfo.Address))
// this.WranDictionary.Remove(pointInfo.Address);
// }
}
}
catch (Exception ex)
{
this.Logger.LogError($"{this.stationName}异常信息[{ex.Message}]");
}
return warnPointList;
}
/// <summary>
/// 扫描PLC报警信息触发事件
/// </summary>
private void ScanPLCWarnList()
{
PLCErrorLists.Clear();
foreach (var plcPoint in WarnPointList)
{
var pointInfo = this.ModbusClient.ReadScannedValue<ushort>(plcPoint.Address);
bool isError = false;
if (pointInfo.Value != 0)
{
for (int i = 0; i < 16; i++)
{
if (((pointInfo.Value >> i) & 1) == 1)
{
var addr = plcPoint.Address.ToString() + "." + i.ToString();
var point = PLCErrorInfoList.FirstOrDefault(p => p.ErrorAddress.Equals(addr));
if (point != null)
{
isError = !point.ErrorInfo.Contains("提示");
PLCErrorLists.Add(point);
}
}
}
if (isError)
{
//给BS那边只需要给报警信息
PLCErrorLists.Add(new PLCErrorPoint() { ErrorAddress = plcPoint.Address.ToString(), ErrorInfo = pointInfo.Value.ToString() }); //这边定义好传给BS的值
}
}
}
if (PLCErrorLists.Count > 0)
{
this.OnScanPlcErrorListEvent?.Invoke(this, PLCErrorLists);
}
}
#endregion
/// <summary>
/// 用于移液枪取运行状态值
/// </summary>
/// <param name="resultString"></param>
/// <returns></returns>
protected string GetPipetteReturnCode(string resultString)
{
try
{
string pattern = @"er(\d{2})";
MatchCollection matches = Regex.Matches(resultString, pattern);
return matches[0].Groups[1].Value;
}
catch (Exception)
{
return resultString;
}
}
}

View File

@ -0,0 +1,461 @@
using Common.Models;
using CommunityToolkit.Mvvm.Messaging;
using MegaRobo.C00225155.Entities;
using MegaRobo.C00225155.Entities.Entity_DB;
using MegaRobo.Contract;
using MegaRobo.ControlDevices;
using MegaRobo.Entities;
using MegaRobo.Logger;
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.Hamilton5mlDevices;
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.HamiltonDevices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Management;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
namespace MegaRobo.C00225155.AppServer.ExecuteWorks
{
/// <summary>
/// 这里主要是针对配置站服务控制1ml移液枪的逻辑
/// </summary>
public partial class StationService_MaterialDosing: StationServiceBase
{
/// <summary>
/// 1ml枪头 Z轴初始化
/// </summary>
/// <returns></returns>
private async Task GunZAxisInit_M1(DeviceNames pipetteDevice, PipetteService pipetteService)
{
this.Logger.LogInformation($"{pipetteDevice}Z轴初始化");
IDictionary<uint, string> dictResult = new Dictionary<uint, string>();
IList<uint> IdList = new List<uint>();
switch (pipetteDevice)
{
case DeviceNames.Pipette_Dose_1ml_1:
IdList = new List<uint> { 1 };
break;
case DeviceNames.Pipette_Dose_1ml_2:
IdList = new List<uint> { 2 };
break;
case DeviceNames.Pipette_Dose_1ml_3:
IdList = new List<uint> { 3 };
break;
case DeviceNames.Pipette_Dose_1ml_4:
IdList = new List<uint> { 4 };
break;
default:
break;
}
pipetteService.Disconnect();
await pipetteService.ConnectionAsync();
dictResult = await pipetteService.InitZaxis(IdList.ToArray());
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{pipetteDevice}Z轴初始化失败,{PipetteErrorDic[errorkey]}");
}
}
/// <summary>
/// 1ml枪头 总初始化
/// </summary>
/// <returns></returns>
private async Task GunAllInit_M1(DeviceNames pipetteDevice, PipetteService pipetteService)
{
this.Logger.LogInformation($"{pipetteDevice}总初始化");
IDictionary<uint, string> dictResult = new Dictionary<uint, string>();
IList<uint> IdList = new List<uint>();
switch (pipetteDevice)
{
case DeviceNames.Pipette_Dose_1ml_1:
IdList = new List<uint> { 1 };
break;
case DeviceNames.Pipette_Dose_1ml_2:
IdList = new List<uint> { 2 };
break;
case DeviceNames.Pipette_Dose_1ml_3:
IdList = new List<uint> { 3 };
break;
case DeviceNames.Pipette_Dose_1ml_4:
IdList = new List<uint> { 4 };
break;
default:
break;
}
dictResult = await pipetteService.InitZaxis(IdList.ToArray());
if (dictResult != null)
{
dictResult = await pipetteService.InitPump();
}
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{pipetteDevice}总初始化失败,{PipetteErrorDic[errorkey]}");
}
}
/// <summary>
/// 1ml枪头 取TIP头
/// </summary>
/// <returns></returns>
private async Task<bool> GunPickUpTip_M1(DeviceNames pipetteDevice, PipetteService pipetteService, TipTypeEnum tipVolume)
{
if (TestMode) return true;
ushort tipType = 7;
uint go = 0;
if (tipVolume.Equals(TipTypeEnum._300UL))//300ul slim的有滤芯的、导电的枪头
{
tipType = 9; go = 0;
}
else if (tipVolume.Equals(TipTypeEnum._50UL)) //50ul的有滤芯的、导电的枪头
{
tipType = 3;
}
else if (tipVolume.Equals(TipTypeEnum._1000UL))
{
tipType = 7;//1000ul的有滤芯的、导电的枪头
}
this.Logger.LogInformation($"{pipetteDevice}取Tip-tipType(9:300,3:50,7:1000):{tipType}");
IDictionary<uint, string> dictResult = new Dictionary<uint, string>();
IList<uint> IdList = new List<uint>();
switch (pipetteDevice)
{
case DeviceNames.Pipette_Dose_1ml_1:
IdList = new List<uint> { 1 };
break;
case DeviceNames.Pipette_Dose_1ml_2:
IdList = new List<uint> { 2 };
break;
case DeviceNames.Pipette_Dose_1ml_3:
IdList = new List<uint> { 3 };
break;
case DeviceNames.Pipette_Dose_1ml_4:
IdList = new List<uint> { 4 };
break;
default:
break;
}
dictResult = await pipetteService.TipInstall(tipType: tipType, go, IdList.ToArray());
await pipetteService.InitZaxis(); //Z轴带动泵模块向上运动到最高处
if (dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00") || dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "76"))
{
this.Logger.LogInformation($"{pipetteDevice}取Tip成功");
return true;
}
else if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
//WeakReferenceMessenger.Default.Send(new ConfirmMessage()
//{
// Header = $"{pipetteDevice}取Tip失败",
// Content = $"{PipetteErrorDic[errorkey]}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
//});
WeakReferenceMessenger.Default.Send(new DesktopAlertMessage() { Header = $"{pipetteDevice}取Tip失败" , Content = $"{PipetteErrorDic[errorkey]}" });
this.Logger.LogError($"{pipetteDevice}取Tip失败,{PipetteErrorDic[errorkey]}");
await GunAllInit_M1(pipetteDevice, pipetteService);
return false;
}
return false;
}
//public async Task ZMove(uint pos)
//{
// IEnumerable<uint> pipetteIdList = new List<uint> { 1 };
// pipetteIdList = pipetteService_1ml.PipetteList.Select(x => x.Id);
// foreach (var id in pipetteIdList)
// {
// var dictRes = await pipetteService_1ml.MoveZaxis(0, id);
// }
//}
/// <summary>
/// 1ml枪头 丢弃Tip头
/// </summary>
/// <returns></returns>
private async Task GunDiscardTip_M1(DeviceNames pipetteDevice, PipetteService pipetteService)
{
if (TestMode) return;
this.Logger.LogInformation($"{pipetteDevice}丢Tip");
IDictionary<uint, string> dictResult = new Dictionary<uint, string>();
IList<uint> IdList = new List<uint>();
switch (pipetteDevice)
{
case DeviceNames.Pipette_Dose_1ml_1:
IdList = new List<uint> { 1 };
break;
case DeviceNames.Pipette_Dose_1ml_2:
IdList = new List<uint> { 2 };
break;
case DeviceNames.Pipette_Dose_1ml_3:
IdList = new List<uint> { 3 };
break;
case DeviceNames.Pipette_Dose_1ml_4:
IdList = new List<uint> { 4 };
break;
default:
break;
}
//var dictResult = await pipetteService_1ml.TipUninstall(1);
//await pipetteService_1ml.InitZaxis();
//if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
//{
// this.Logger.LogError($"{DeviceNames.Pipette_Config_1ml}丢Tip失败,{dictResult.LastOrDefault().Value}");
//}
dictResult = await pipetteService.InitPump(IdList.ToArray());
await pipetteService.InitZaxis();
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{pipetteDevice}丢Tip失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
this.Logger.LogError($"{pipetteDevice}丢Tip失败,{PipetteErrorDic[errorkey]}");
await GunAllInit_M1(pipetteDevice, pipetteService);
}
}
private async Task GunAbsorb_M1(DeviceNames pipetteDevice, PipetteService pipetteService, string liquidName, int tiporder, double absorbVolumn, SourceLiquidBottleModel sourceLiquidBottleModel)
{
if (TestMode) return;
IDictionary<uint, string> dictResult = new Dictionary<uint, string>();
var curLiqud = await this.dataAccessService.LoadEntitiesAsync<Liquid_lml_Dose>(x => x.LiquidName == liquidName && x.TipType == tiporder); //&& x.TipType == tiporder 暂时屏蔽
if (curLiqud == null || curLiqud.Count == 0)
{
curLiqud = await this.dataAccessService.LoadEntitiesAsync<Liquid_lml_Dose>(x => x.TipType == tiporder);
}
var curLiqud_M1 = curLiqud.FirstOrDefault();
curLiqud_M1.TipType = tiporder;
curLiqud_M1.LiquidVolume = (int)Math.Abs(absorbVolumn * curLiqud_M1.Ratio);
IList<uint> IdList = new List<uint>();
switch (pipetteDevice)
{
case DeviceNames.Pipette_Dose_1ml_1:
IdList = new List<uint> { 1 };
break;
case DeviceNames.Pipette_Dose_1ml_2:
IdList = new List<uint> { 2 };
break;
case DeviceNames.Pipette_Dose_1ml_3:
IdList = new List<uint> { 3 };
break;
case DeviceNames.Pipette_Dose_1ml_4:
IdList = new List<uint> { 4 };
break;
default:
break;
}
this.Logger.LogInformation($"{pipetteDevice}开始吸液,吸液量{curLiqud_M1.LiquidVolume}ul");
dictResult = await OnSuckFluid(pipetteService, IdList, curLiqud_M1, 1, 1, sourceLiquidBottleModel);
//for (int i = 1; i < 3; i++) //若吸液失败,二次吸液
{
if (dictResult != null)
{
if (dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
this.Logger.LogInformation($"{pipetteDevice}吸液成功");
await pipetteService.InitZaxis();
}
else
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{pipetteDevice}吸液失败,{PipetteErrorDic[errorkey]}");
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{pipetteDevice}吸液失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
await pipetteService.InitZaxis();
}
}
else
{
await pipetteService.InitZaxis();
}
}
}
/// <summary>
/// 1ml枪头 分液
/// </summary>
/// <param name="liquidName">液体名称</param>
/// <param name="volumn">分液体积</param>
/// <returns></returns>
private async Task GunDispensing_M1(DeviceNames pipetteDevice, PipetteService pipetteService, string liquidName, int tipOrder,uint vol, SampleBottleModel sampleBottleModel)
{
if (TestMode) return;
IDictionary<uint, string> dictResult = new Dictionary<uint, string>();
//IList<uint> listId = new List<uint>();
Liquid_lml_Dose curLiqud_M1 = new Liquid_lml_Dose();
IList<uint> IdList = new List<uint>();
switch (pipetteDevice)
{
case DeviceNames.Pipette_Dose_1ml_1:
IdList = new List<uint> { 1 };
break;
case DeviceNames.Pipette_Dose_1ml_2:
IdList = new List<uint> { 2 };
break;
case DeviceNames.Pipette_Dose_1ml_3:
IdList = new List<uint> { 3 };
break;
case DeviceNames.Pipette_Dose_1ml_4:
IdList = new List<uint> { 4 };
break;
default:
break;
}
this.Logger.LogInformation($"{pipetteDevice}开始分液");
var curLiqud = await this.dataAccessService.LoadEntitiesAsync<Liquid_lml_Dose>(x => x.LiquidName == liquidName && x.TipType == tipOrder); //
if (curLiqud == null || curLiqud.Count == 0)
{
curLiqud = await this.dataAccessService.LoadEntitiesAsync<Liquid_lml_Dose>(x => x.TipType == tipOrder);
}
curLiqud_M1 = curLiqud.FirstOrDefault();
if (sampleBottleModel.SampleBottleType == SampleBottleTypeEnum._12mL)
{
curLiqud_M1.DispenHeight -= 300; //12ml样品瓶比5ml高约3cm
}
dictResult = await OnDispenFluid(pipetteService, IdList, curLiqud_M1, vol);//(uint)Math.Abs(volumn)
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{pipetteDevice}分液失败,{PipetteErrorDic[errorkey]}");
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{pipetteDevice}分液失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
await pipetteService.InitZaxis();
}
else
{
this.Logger.LogInformation($"{pipetteDevice}分液成功");
await pipetteService.InitZaxis();
}
}
private async Task<IDictionary<uint, string>> OnSuckFluid(PipetteService pipetteService, IList<uint> listId, Liquid_lml_Dose liquidProduct, int subid, int asperIndex = 1, SourceLiquidBottleModel sourceLiquidBottleModel = null)
{
IDictionary<uint, string> resultData;
try
{
if (liquidProduct == null) return null;
uint platformCode = (uint)liquidProduct.PlatformCode;//平台平台编号
uint liquidReagentCode = (uint)liquidProduct.LiquidParamId; //移液枪液体编号
uint vol = (uint)liquidProduct.LiquidVolume;//吸液体积
uint ge = (uint)liquidProduct.ContainerPlateIndex;//容器编号
uint lld = (uint)liquidProduct.LiqLedetec;//是否液面探测
uint pos = (uint)liquidProduct.AspireHeight; //液面探测其实高度 或者固定吸液高度
if (lld == 0) //qrbian todo:需要区分1000μl,300μl,50μl,计算余量对应的吸液高度 根据sourceLiquidBottleModel sourceLiquidBottleModel.RemainVolume
{
//底部高度三种tip不一样 = 底部高度 - 液体剩余高度 + 浸入高度三种tip不一样
//int heightMax = liquidProduct.TipType == 3 ? 1000 : (liquidProduct.TipType == 2 ? 300 : 50); //tip到底部高度最大值
int heightMax = 2220;
//int inHeight = liquidProduct.TipType == 3 ? 1000 : (liquidProduct.TipType == 2 ? 300 : 50); //浸入高度
int inHeight = 50; //浸入高度
if (sourceLiquidBottleModel.RemainVolume > 40)
{
sourceLiquidBottleModel.RemainVolume = 40;
}
int liquidRemainHeight = (int)(100 * sourceLiquidBottleModel.RemainVolume / (Math.PI * 1.25 * 1.25));
int realHeight = heightMax - liquidRemainHeight + inHeight; //底部高度向上抬升液体高度再浸入液面高度
realHeight = realHeight > heightMax ? heightMax : realHeight; //确保不能超过底部高度
//pos = 1700; //todo:将上面的heightMax、inHeight修改这个替换成realHeight
pos = (uint)realHeight;
}
IList<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)> idArrayParams = new List<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)>();
foreach (var id in listId)
{
idArrayParams.Add((id, vol, liquidReagentCode, pos, ge, platformCode, lld));
}
resultData = await pipetteService.SuckFluid(idArrayParams);
}
catch (Exception ex)
{
this.Logger.LogError($"{DeviceNames.Pipette_Dose_1ml_1}吸液异常:{ex}");
throw;
}
return resultData;
}
private async Task<IDictionary<uint, string>> OnDispenFluid1(PipetteService pipetteService, IList<uint> listId, Liquid_lml_Dose liquidProduct, uint dispos)
{
IDictionary<uint, string> resultData = new Dictionary<uint, string>();
try
{
if (liquidProduct == null) return null;
uint platformCode = (uint)liquidProduct.PlatformCode;//平台平台编号
uint liquidReagentCode = (uint)liquidProduct.LiquidParamId; //移液枪液体编号
uint vol = (uint)liquidProduct.LiquidVolume;//吸液体积
uint ge = (uint)liquidProduct.ContainerPlateIndex;//容器编号
uint lld = (uint)liquidProduct.LiqLedetec;//是否液面探测
uint pos = dispos;//吸液高度
IList<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)> idArrayParams = new List<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)>();
foreach (var id in listId)
{
idArrayParams.Add((id, vol, liquidReagentCode, pos, ge, platformCode, 0));
}
resultData = await pipetteService.DrainageFluid(idArrayParams);
}
catch (Exception ex)
{
this.Logger.LogError($"{DeviceNames.Pipette_Dose_1ml_1}分液异常,异常原因:{ex}");
}
return resultData;
}
private async Task<IDictionary<uint, string>> OnDispenFluid(PipetteService pipetteService, IList<uint> listId, Liquid_lml_Dose liquidProduct, uint volume)
{
IDictionary<uint, string> resultData = new Dictionary<uint, string>();
try
{
if (liquidProduct == null) return null;
uint platformCode = (uint)liquidProduct.PlatformCode;//平台平台编号
uint liquidReagentCode = (uint)liquidProduct.LiquidParamId;//移液枪液体编号
uint ge = (uint)liquidProduct.ContainerPlateIndex;//容器编号
uint lld = (uint)liquidProduct.LiqLedetec;//是否液面探测
uint pos = (uint)liquidProduct.DispenHeight;//分液高度
uint vol = volume;
IList<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)> idArrayParams = new List<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)>();
foreach (var id in listId)
{
idArrayParams.Add((id, vol, liquidReagentCode, pos, ge, platformCode, 0));
}
resultData = await pipetteService.DrainageFluid(idArrayParams);
}
catch (Exception ex)
{
this.Logger.LogError($"{DeviceNames.Pipette_Dose_1ml_1}分液异常,异常原因:{ex.ToString()}");
}
return resultData;
}
}
}

View File

@ -0,0 +1,167 @@
using Common;
using Common.Models;
using CommunityToolkit.Mvvm.Messaging;
using MegaRobo.C00225155.ControlDevices;
using MegaRobo.C00225155.ControlDevices.Scanner;
using MegaRobo.C00225155.ControlDevices.WeightDevice;
using MegaRobo.C00225155.Entities;
using MegaRobo.C00225155.Entities.Entity_DB;
using MegaRobo.Contract;
using MegaRobo.Logger;
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.HamiltonDevices;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Pipette1ml = MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.HamiltonDevices;
using Pipette5ml = MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.Hamilton5mlDevices;
using Task = System.Threading.Tasks.Task;
using MegaRobo.ControlDevices.Models;
using NetTaste;
using System.Threading.Tasks;
using MegaRobo.Contract.Abstractions;
using MegaRobo.ControlDevices;
using MegaRobo.Entities;
using Nito.AsyncEx;
namespace MegaRobo.C00225155.AppServer.ExecuteWorks
{
public partial class StationService_ReactionProcessing : StationServiceBase
{
private IModbusClientExtend _modbusClient_Plc2;
public StationProperty StationProp = new StationProperty();
#region
protected ScannerService scannerService_react;
private PipetteDictionary pipetteDictionary { get; set; }
private Pipette1ml.PipetteService pipetteService_1ml { get; set; }
#endregion
#region events
#endregion
public StationService_ReactionProcessing(ICoreService coreService, WorkService workService, DeviceNames plcName, string station) : base(coreService, workService, plcName, station)
{
//Initial();
}
protected override void LoadServices()
{
base.LoadServices();
scannerService_react = this.CoreService.GetServiceInstance<ScannerService>(DeviceNames.Scanner_ReactRack.ToString());
pipetteService_1ml = this.CoreService.GetServiceInstance<Pipette1ml.PipetteService>(DeviceNames.Pipette_React_1ml.ToString());
pipetteDictionary = new PipetteDictionary();
//_modbusClient_Plc2 = this.CoreService.GetServiceInstance<IModbusClientExtend>(DeviceNames.Plc2.ToString());
if (this.ModbusClient != null)
{
//this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_React.W_BoxPlateUploadFinish, 0);
//this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_React.W_DnLoad, 0);
//this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_React.W_BoxPlatePos, 0);
//this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_React.W_BoxPlatePutFinish, 0);
//this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_React.W_BoxPlateCave1have, 0);
//this.ModbusClient.Command.WriteValue<ushort>(PlcPoints_React.W_BoxPlateCave2have, 0);
}
}
protected override async void ModbusClientBaseOnScannedEvent(object sender, EventArgs e)
{
base.ModbusClientBaseOnScannedEvent(sender, e);
this.StationProp.ManualResetEvent.WaitOne(); //暂停标志
}
/// <summary>
/// 计算枪头吸液次数
/// </summary>
/// <param name="volumn"></param>
/// <returns></returns>
private List<double> CalculateAbsorbCount(double volumn)
{
List<double> counts = new List<double>();
int count = (int)(volumn / 5000);
for (int i = 0; i < count; i++)
{
counts.Add(5000);
}
if (volumn % 5000 > 0)
{
counts.Add(volumn % 5000);
}
return counts;
}
private async Task SetObstructFlag(string name, bool value)
{
try
{
if (this.StationProp.ObstructFlagList.TryGetValue(name, out var targetFlag))
{
if (targetFlag != null)
{
lock (targetFlag)
{
targetFlag.flag = value;
}
}
}
}
catch (Exception)
{
//throw;
}
}
private async Task<bool> ReadObstructFlag(string name, bool value = true, bool bAny = false)
{
try
{
if (!bAny)
{
if (this.StationProp.ObstructFlagList.TryGetValue(name, out var targetFlag))
{
if (targetFlag != null)
{
lock (targetFlag)
{
return targetFlag.flag == value;
}
}
}
}
else if (bAny)
{
foreach (var pair in this.StationProp.ObstructFlagList)
{
if (pair.Key.Contains(name))
{
lock (pair.Value)
{
if (pair.Value.flag == value)
{
return true;
}
}
}
}
}
}
catch (Exception)
{
return false;
}
return false;
}
}
}

View File

@ -0,0 +1,302 @@
using CommunityToolkit.Mvvm.Messaging;
using MegaRobo.C00225155.Entities;
using MegaRobo.C00225155.Entities.Entity_DB;
using MegaRobo.Contract;
using MegaRobo.ControlDevices;
using MegaRobo.Entities;
using MegaRobo.Logger;
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.Hamilton5mlDevices;
using MegaRobo.PipetteTool.HamiltonConsole.PipetteDevices.HamiltonDevices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
namespace MegaRobo.C00225155.AppServer.ExecuteWorks
{
/// <summary>
/// 这里主要是针对配置站服务控制1ml移液枪的逻辑
/// </summary>
public partial class StationService_ReactionProcessing : StationServiceBase
{
/// <summary>
/// 1ml枪头 Z轴初始化
/// </summary>
/// <returns></returns>
private async Task GunZAxisInit_M1()
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}Z轴初始化");
//莫名其妙的每次初始化都需要重新连接一次,不知道为什么
pipetteService_1ml.Disconnect();
await pipetteService_1ml.ConnectionAsync();
var dictResult = await pipetteService_1ml.InitZaxis();
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}Z轴初始化失败,{PipetteErrorDic[errorkey]}");
}
}
/// <summary>
/// 1ml枪头 总初始化
/// </summary>
/// <returns></returns>
private async Task GunAllInit_M1()
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}总初始化");
var dictResult = await pipetteService_1ml.InitZaxis();
if (dictResult != null)
{
dictResult = await pipetteService_1ml.InitPump();
}
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}总初始化失败,{PipetteErrorDic[errorkey]}");
}
}
/// <summary>
/// 1ml枪头 取TIP头
/// </summary>
/// <returns></returns>
private async Task<bool> GunPickUpTip_M1(double tipVolumn)
{
ushort tipType = 7;
if (tipVolumn == 300)//300ul slim的有滤芯的、导电的枪头
{
tipType = 9;
}
else if (tipVolumn == 50) //50ul的有滤芯的、导电的枪头
{
tipType = 3;
}
else
{
tipType = 7;//1000ul的有滤芯的、导电的枪头
}
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}取Tip");
var dictResult = await pipetteService_1ml.TipInstall(tipType: tipType);
await pipetteService_1ml.InitZaxis(); //Z轴带动泵模块向上运动到最高处
if (dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00") || dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "76"))
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}取Tip成功");
return true;
}
else if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{DeviceNames.Pipette_React_1ml}取Tip失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}取Tip失败,{PipetteErrorDic[errorkey]}");
await GunAllInit_M1();
return false;
}
return false;
}
public async Task ZMove(uint pos)
{
IEnumerable<uint> pipetteIdList = new List<uint> { 1 };
pipetteIdList = pipetteService_1ml.PipetteList.Select(x => x.Id);
foreach (var id in pipetteIdList)
{
var dictRes = await pipetteService_1ml.MoveZaxis(0, id);
}
}
/// <summary>
/// 1ml枪头 丢弃Tip头
/// </summary>
/// <returns></returns>
private async Task GunDiscardTip_M1()
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}丢Tip");
//var dictResult = await pipetteService_1ml.TipUninstall(1);
//await pipetteService_1ml.InitZaxis();
//if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
//{
// this.Logger.LogError($"{DeviceNames.Pipette_Config_1ml}丢Tip失败,{dictResult.LastOrDefault().Value}");
//}
var dictResult = await pipetteService_1ml.InitPump();
await pipetteService_1ml.InitZaxis();
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{DeviceNames.Pipette_React_1ml}丢Tip失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}丢Tip失败,{PipetteErrorDic[errorkey]}");
await GunAllInit_M1();
}
}
private async Task GunAbsorb_M1(string funcType, string liquidName, int tiporder, double absorbVolumn)
{
IList<uint> listId = new List<uint>() { 1 };
var curLiqud = await this.dataAccessService.LoadEntitiesAsync<Liquid_lml_React>(x => x.FuncType == funcType && x.LiquidName == liquidName && (x.TipType == tiporder));
var curLiqud_M1 = curLiqud.FirstOrDefault();
curLiqud_M1.LiquidVolume = (int)Math.Abs(absorbVolumn * curLiqud_M1.Ratio);
//for (int i = 1; i < 3; i++) //若吸液失败,二次吸液
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}开始吸液,吸液量{curLiqud_M1.LiquidVolume}ul");
var dictResult = await OnSuckFluid(pipetteService_1ml, listId, curLiqud_M1, 1, 1);
if (dictResult != null)
{
if (dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}吸液成功");
await pipetteService_1ml.InitZaxis();
}
else
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}吸液失败,{PipetteErrorDic[errorkey]}");
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{DeviceNames.Pipette_React_1ml}吸液失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
await pipetteService_1ml.InitZaxis();
}
}
else
{
await pipetteService_1ml.InitZaxis();
}
}
}
/// <summary>
/// 1ml枪头 分液
/// </summary>
/// <param name="liquidName">液体名称</param>
/// <param name="volumn">分液体积</param>
/// <returns></returns>
private async Task GunDispensing_M1(string funcType, string liquidName, int tipOrder, double volumn)
{
Liquid_lml_React curLiqud_M1 = new Liquid_lml_React();
IList<uint> listId = new List<uint>() { 1 };
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}开始分液,分液量{volumn}ul");
var curLiqud = await this.dataAccessService.LoadEntitiesAsync<Liquid_lml_React>(x => x.FuncType == funcType && x.LiquidName == liquidName && (x.TipType == tipOrder));
curLiqud_M1 = curLiqud.FirstOrDefault();
var dictResult = await OnDispenFluid(pipetteService_1ml, listId, curLiqud_M1, 100);//(uint)Math.Abs(volumn)
if (!dictResult.All(x => this.GetPipetteReturnCode(x.Value) == "00"))
{
string errorkey = this.GetPipetteReturnCode(dictResult.LastOrDefault().Value);
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}分液失败,{PipetteErrorDic[errorkey]}");
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"{DeviceNames.Pipette_React_1ml}分液失败",
Content = $"{PipetteErrorDic[errorkey]}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
await pipetteService_1ml.InitZaxis();
}
else
{
this.Logger.LogInformation($"{DeviceNames.Pipette_React_1ml}分液成功");
await pipetteService_1ml.InitZaxis();
}
}
private async Task<IDictionary<uint, string>> OnSuckFluid(PipetteService pipetteService, IList<uint> listId, Liquid_lml_React liquidProduct, int subid, int asperIndex = 1)
{
IDictionary<uint, string> resultData;
try
{
if (liquidProduct == null) return null;
uint platformCode = (uint)liquidProduct.PlatformCode;//平台平台编号
uint liquidReagentCode = (uint)liquidProduct.LiquidParamId; //移液枪液体编号
uint vol = (uint)liquidProduct.LiquidVolume;//吸液体积
uint ge = (uint)liquidProduct.ContainerPlateIndex;//容器编号
uint lld = (uint)liquidProduct.LiqLedetec;//是否液面探测
uint pos = (uint)liquidProduct.AspireHeight;
IList<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)> idArrayParams = new List<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)>();
foreach (var id in listId)
{
idArrayParams.Add((id, vol, liquidReagentCode, pos, ge, platformCode, lld));
}
resultData = await pipetteService.SuckFluid(idArrayParams);
}
catch (Exception ex)
{
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}吸液异常:{ex}");
throw;
}
return resultData;
}
private async Task<IDictionary<uint, string>> OnDispenFluid1(PipetteService pipetteService, IList<uint> listId, Liquid_lml_React liquidProduct, uint dispos)
{
IDictionary<uint, string> resultData = new Dictionary<uint, string>();
try
{
if (liquidProduct == null) return null;
uint platformCode = (uint)liquidProduct.PlatformCode;//平台平台编号
uint liquidReagentCode = (uint)liquidProduct.LiquidParamId; //移液枪液体编号
uint vol = (uint)liquidProduct.LiquidVolume;//吸液体积
uint ge = (uint)liquidProduct.ContainerPlateIndex;//容器编号
uint lld = (uint)liquidProduct.LiqLedetec;//是否液面探测
uint pos = dispos;//吸液高度
IList<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)> idArrayParams = new List<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)>();
foreach (var id in listId)
{
idArrayParams.Add((id, vol, liquidReagentCode, pos, ge, platformCode, 0));
}
resultData = await pipetteService.DrainageFluid(idArrayParams);
}
catch (Exception ex)
{
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}分液异常,异常原因:{ex}");
}
return resultData;
}
private async Task<IDictionary<uint, string>> OnDispenFluid(PipetteService pipetteService, IList<uint> listId, Liquid_lml_React liquidProduct, uint volume)
{
IDictionary<uint, string> resultData = new Dictionary<uint, string>();
try
{
if (liquidProduct == null) return null;
uint platformCode = (uint)liquidProduct.PlatformCode;//平台平台编号
uint liquidReagentCode = (uint)liquidProduct.LiquidParamId;//移液枪液体编号
uint ge = (uint)liquidProduct.ContainerPlateIndex;//容器编号
uint lld = (uint)liquidProduct.LiqLedetec;//是否液面探测
uint pos = (uint)liquidProduct.DispenHeight;//分液高度
uint vol = volume;
IList<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)> idArrayParams = new List<(uint id, uint vol, uint lq, uint pos, uint ge, uint go, uint lb)>();
foreach (var id in listId)
{
idArrayParams.Add((id, vol, liquidReagentCode, pos, ge, platformCode, 0));
}
resultData = await pipetteService.DrainageFluid(idArrayParams);
}
catch (Exception ex)
{
this.Logger.LogError($"{DeviceNames.Pipette_React_1ml}分液异常,异常原因:{ex.ToString()}");
}
return resultData;
}
}
}

View File

@ -0,0 +1,582 @@
using Common.Models;
using CommunityToolkit.Mvvm.Messaging;
using MegaRobo.Entities;
using MegaRobo.Logger;
using Newtonsoft.Json.Linq;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Automation.Text;
using System.Windows.Data;
namespace MegaRobo.C00225155.AppServer.ExecuteWorks
{
/// <summary>
/// 这里主要写检测工站的和PLC通讯的函数
/// </summary>
public partial class StationService_ReactionProcessing : StationServiceBase
{
//#region PLC点位写/读
///// <summary>
///// 旋转平台---等待信号的锁
///// </summary>
//private static readonly AsyncLock _lockWaitSignal_Rotater = new AsyncLock();
//private static readonly AsyncLock _lockWaitSignal_SixAxis = new AsyncLock();
//private static readonly AsyncLock _lockWaitSignal_XYZ = new AsyncLock();
//private static readonly AsyncLock _lockWaitSignal_XZ = new AsyncLock();
//private static readonly AsyncLock _lockWaitSignal_Upload = new AsyncLock();
//private static readonly AsyncLock _lockWaitSignal_Dnload = new AsyncLock();
//private static readonly AsyncLock _lockDetect_Electricty = new AsyncLock();
//private static readonly AsyncLock _lockDetect_Acid = new AsyncLock();
//private static readonly AsyncLock _lockDetect_Moisture = new AsyncLock();
//private static readonly AsyncLock _lockSixAxisDoAction = new AsyncLock();
//public async Task<ushort> ReadScannedValueAsync_Upload(ushort address)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// return this.ModbusClient.Command.ReadValue<ushort>(address);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站读取PLC信号异常",
// Content = $"ReadUpload读取 {address} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"ReadScannedValueAsync_Upload读取 {address} 时发生错误: {ex.Message}");
// }
// return 0;
//}
//public async Task<ushort> ReadScannedValueAsync_Dnload(ushort address)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// return this.ModbusClient.Command.ReadValue<ushort>(address);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站读取PLC信号异常",
// Content = $"ReadDnload读取 {address} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"ReadScannedValueAsync_Dnload读取 {address} 时发生错误: {ex.Message}");
// }
// return 0;
//}
//public async Task<ushort> ReadScannedValueAsync_SixAxis(ushort address)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// return this.ModbusClient.Command.ReadValue<ushort>(address);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站读取PLC信号异常",
// Content = $"ReadSixAxis读取 {address} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"ReadScannedValueAsync_SixAxis读取 {address} 时发生错误: {ex.Message}");
// }
// return 0;
//}
//public async Task<ushort> ReadScannedValueAsync_XYZ(ushort address)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// return this.ModbusClient.Command.ReadValue<ushort>(address);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站读取PLC信号异常",
// Content = $"ReadXYZ读取 {address} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"ReadScannedValueAsync_XYZ读取 {address} 时发生错误: {ex.Message}");
// }
// return 0;
//}
//public async Task<ushort> ReadScannedValueAsync_XZ(ushort address)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// return this.ModbusClient.Command.ReadValue<ushort>(address);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站读取PLC信号异常",
// Content = $"ReadXZ读取 {address} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"ReadScannedValueAsync_XZ读取 {address} 时发生错误: {ex.Message}");
// }
// return 0;
//}
//public async Task WriteCommandAsync_Upload(ushort address, ushort value)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// //await Task.Delay(30);
// while (await ReadScannedValueAsync_Upload(address) != value)
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// await Task.Delay(30);
// }
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站写入PLC信号异常",
// Content = $"WriteUpload写入 {address}值{value} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WriteCommandAsync_Upload写入 {address}值{value} 时发生错误: {ex.Message}");
// return;
// }
//}
//public async Task WriteCommandAsync_Dnload(ushort address, ushort value)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// //await Task.Delay(30);
// while (await ReadScannedValueAsync_Dnload(address) != value)
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// await Task.Delay(30);
// }
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站写入PLC信号异常",
// Content = $"WriteDnload写入 {address}值{value} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WriteCommandAsync_Dnload写入 {address}值{value} 时发生错误: {ex.Message}");
// return;
// }
//}
//public async Task WriteCommandAsync_Six(ushort address, ushort value)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// //await Task.Delay(30);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站写入PLC信号异常",
// Content = $"WriteSix写入 {address}值{value} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WriteCommandAsync_Six写入 {address}值{value} 时发生错误: {ex.Message}");
// return;
// }
//}
//public async Task WriteCommandAsync_SixAxis(ushort address, ushort value)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// //await Task.Delay(30);
// while (await ReadScannedValueAsync_SixAxis(address) != value)
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// await Task.Delay(30);
// }
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站写入PLC信号异常",
// Content = $"WriteSixAxis写入 {address}值{value} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WriteCommandAsync_SixAxis写入 {address}值{value} 时发生错误: {ex.Message}");
// return;
// }
//}
//public async Task WriteCommandAsync_XYZ(ushort address, ushort value)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// //await Task.Delay(30);
// while (await ReadScannedValueAsync_XYZ(address) != value)
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// await Task.Delay(30);
// }
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站写入PLC信号异常",
// Content = $"WriteXYZ写入 {address}值{value} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WriteCommandAsync_XYZ写入 {address}值{value} 时发生错误: {ex.Message}");
// return;
// }
//}
//public async Task WriteCommandAsync_XZ(ushort address, ushort value)
//{
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// try
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// //await Task.Delay(30);
// while (await ReadScannedValueAsync_XZ(address) != value)
// {
// this.ModbusClient.Command.WriteValue<ushort>(address, value);
// await Task.Delay(30);
// }
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站写入PLC信号异常",
// Content = $"WriteXZ写入 {address}值{value} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WriteCommandAsync_XZ写入 {address}值{value} 时发生错误: {ex.Message}");
// return;
// }
//}
//public async Task WaitForActionFinishAsync_Upload(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockWaitSignal_Upload.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_SixAxis(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForUpload等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_Upload等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_DnLoad(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockWaitSignal_Dnload.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_SixAxis(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForDnLoad等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_DnLoad等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_SixAxis(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockWaitSignal_SixAxis.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_SixAxis(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForSixAxis等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_SixAxis等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_Electricty(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockDetect_Electricty.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_SixAxis(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForElectricty等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_Electricty等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_Acid(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockDetect_Acid.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_SixAxis(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForAcid等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_Acid等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_Moisture(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockDetect_Moisture.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_SixAxis(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForMoisture等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_Moisture等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_XYZ(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockWaitSignal_XYZ.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_XYZ(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForXYZ等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_XYZ等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//public async Task WaitForActionFinishAsync_XZ(ushort point, ushort waitValue, CancellationToken cancellationToken)
//{
// using (await _lockWaitSignal_XZ.LockAsync(cancellationToken))
// {
// try
// {
// ushort result = 0;
// do
// {
// appConfigService.StationPropList[1].ManualResetEvent.WaitOne(); //暂停标志
// result = await ReadScannedValueAsync_XZ(point);
// await Task.Delay(100, cancellationToken);
// } while (result != waitValue && !cancellationToken.IsCancellationRequested);
// }
// catch (Exception ex)
// {
// WeakReferenceMessenger.Default.Send(new ConfirmMessage()
// {
// Header = $"检测站等待PLC信号异常",
// Content = $"WaitForXZ等待{point}是否是{waitValue} 时发生错误: {ex.Message}",
// OkButtonContent = "继续",
// CancelButtonContent = "取消",
// });
// this.Logger.LogError($"WaitForActionFinishAsync_XZ等待{point}是否是{waitValue} 时发生错误: {ex.Message}");
// return;
// }
// }
//}
//#endregion
private async Task RefreshReactBottleState(int boxId, int posId, SampleBottleStateEnum state)
{
//if (workService.ProjectPro.ReactBottleBoxList.FirstOrDefault(f => f.BoxId == boxId) != null)
//{
// SampleBottleBoxModel reactBottleBoxModel = workService.ProjectPro.ReactBottleBoxList.FirstOrDefault(f => f.BoxId == boxId);
// if (reactBottleBoxModel.ReactBottleList.FirstOrDefault(f => f.PosId_InBox - 1 == posId) != null)
// {
// reactBottleBoxModel.ReactBottleList.FirstOrDefault(f => f.PosId_InBox - 1 == posId).ReactBoxState = state;
// }
//}
////更新界面显示结果
//SampleBottleModel bottleModel = null;
//bottleModel = workService.ProjectPro.ReactBottleBoxListByOrder[boxId - 1].ReactBottleList.FirstOrDefault(f => f.PosId_InBox - 1 == posId);
//if (state == ReactBottleStateEnum.Detecting)
//{
// if (!OngoingDetectResultList.Any(f => f.BoxId == bottleModel.BoxId && f.BottleId == bottleModel.PosId_InBox))
// {
// ReactBottleResult_Show result = new ReactBottleResult_Show
// {
// Guid = bottleModel.reactBottleResult.Guid,
// BoxId = bottleModel.reactBottleResult.BoxId,
// BottleId = bottleModel.reactBottleResult.BottleId,
// SampleName = bottleModel.reactBottleResult.SampleName,
// bDetectFinish = false
// };
// OngoingDetectResultList.Add(result);
// UpdateResultEvent?.Invoke(this, OngoingDetectResultList);
// }
//}
//else if (state == ReactBottleStateEnum.FinishDetect)
//{
// if (OngoingDetectResultList.Any(f => f.BoxId == bottleModel.BoxId && f.BottleId == bottleModel.PosId_InBox))
// {
// ReactBottleResult_Show result = OngoingDetectResultList.FirstOrDefault(f => f.BoxId == bottleModel.BoxId && f.BottleId == bottleModel.PosId_InBox && f.Guid == bottleModel.reactBottleResult.Guid);
// if (result != null)
// {
// result.bDetectFinish = true;
// //OngoingDetectResultList.Add(result);
// UpdateResultEvent?.Invoke(this, OngoingDetectResultList);
// }
// }
//}
await Task.Delay(1);
}
}
}

View File

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
<PackageReference Include="NETStandard.HttpListener" Version="1.0.3.5" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\MegaRobo.Common\MegaRobo.ControlDevices\MegaRobo.ControlDevices.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\MegaRobo.C00225155.ControlDevices\MegaRobo.C00225155.ControlDevices.csproj" />
<ProjectReference Include="..\MegaRobo.C00225155.DataAccess\MegaRobo.C00225155.DataAccess.csproj" />
<ProjectReference Include="..\MegaRobo.C00225155.Entities\MegaRobo.C00225155.Entities.csproj" />
<ProjectReference Include="..\MegaRobo.RRQuartz\MegaRobo.RRQuartz.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="MegaRobo.Contract">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Contract.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.DataAccess">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.DataAccess.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Entities">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Entities.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Logger">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Logger.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.PipetteTool.HamiltonConsole">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.PipetteTool.HamiltonConsole.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Utility">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Utility.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,100 @@
using Common;
using Common.Models;
using CommunityToolkit.Mvvm.Messaging;
using LightInject;
using MegaRobo.C00225155.AppServer.ExecuteWorks;
using MegaRobo.C00225155.ControlDevices;
using MegaRobo.C00225155.DataAccess;
using MegaRobo.C00225155.Entities;
using MegaRobo.Contract;
using MegaRobo.Contract.Abstractions;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using MegaRobo.RRQuartz;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Net;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.AppServer;
/// <summary>
/// 这里面管理所有的StationServices
/// </summary>
public partial class WorkService : WorkServiceBase
{
// 当ProjectPro变更时触发的事件
public event Action ProjectProChanged;
public event EventHandler TickEvent;
private AppConfigService _appConfigService;
//private IDataAccessService _dataAccessService;
/// <summary>
/// 工站集合
/// </summary>
public IList<StationServiceBase> stationList;
/// <summary>
/// 投料站
/// </summary>
public StationService_MaterialDosing stationService_Dose;
/// <summary>
/// 反应站
/// </summary>
public StationService_ReactionProcessing stationService_React;
private ProjectProperty _projectPro = new ProjectProperty();
public ProjectProperty ProjectPro
{
get { return _projectPro; }
set
{
if (_projectPro != value)
{
_projectPro = value;
ProjectProChanged?.Invoke(); //触发事件
}
}
}
public override void Initialize(ICoreService coreService, params object[] args)
{
this.CoreService = coreService;
base.Initialize(coreService, args);
#region webapi相关
HttpListenerProvider _httpListenerProvider = this.CoreService.GetServiceInstance<HttpListenerProvider>();
this._httpListenerService = _httpListenerProvider.GetOrAdd(this._appConfigService.ProjectInfo.Code, null);
this.RegiondRoute("A1");
this.AddSwaggerSupport();
#endregion
stationService_Dose = new StationService_MaterialDosing(CoreService, this, DeviceNames.Plc1, "Station_Dose");
stationService_Dose.Initialize(this.CoreService);
//stationService_React = new StationService_ReactionProcessing(CoreService, this, DeviceNames.Plc2, "Station_React");
//stationService_React.Initialize(this.CoreService);
}
protected override void LoadServices()
{
this._appConfigService = this.CoreService.GetServiceInstance<AppConfigService>();
//this._dataAccessService = this.CoreService.GetServiceInstance<IDataAccessService>();
}
protected override void LoadDatas()
{
base.LoadDatas();
}
protected override void AttachEvents()
{
base.AttachEvents();
}
}

View File

@ -0,0 +1,939 @@
using MegaRobo.C00225155.Entities.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.ServiceModel;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class AACWebApiService
{
public string _baseUrl { get; set; }
public string AuthToken { get; set; }
public HttpOperateHelper httpClient { get; set; }
public JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
};
#region Authentication
/// <summary>
/// 登录系统并获取Token
/// </summary>
/// <param name="userName">用户名</param>
/// <param name="userPassword">密码</param>
/// <returns>包含Token的响应模型</returns>
public async Task<Response<LoginResponse>> Login(string userName, string userPassword)
{
var requestUrl = $"{_baseUrl}/api/v1/Authentication?userName={userName}&userPassword={userPassword}";
try
{
return await httpClient.PutAsync<LoginResponse>(requestUrl, null);
}
catch (HttpRequestException ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
#endregion
#region Instrument
/// <summary>
/// 开启/关闭模块相关启用状态
/// </summary>
/// <param name="instrumentid">仪器Id</param>
/// <param name="moduleIdentity">仪器模块Id</param>
/// <param name="state">要设置的状态 0: Standby/off 1: on</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> State(int instrumentid, string moduleIdentity,int state)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/Module/State?cdsId={instrumentid}&moduleIdentity={moduleIdentity}&state={state}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, null);
}
catch (HttpRequestException ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 重置LC馏分收集器容器
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="containerName"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<string>> LCContainerReset(int instrumentid, string containerName)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/SubDevice/Drawer/Container/Volume/Reset?cdsId={instrumentid}&containerName={containerName}";
try
{
return await httpClient.PutAsync<string>(requestUrl, null);
}
catch (HttpRequestException ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 重置LC馏分收集器子设备
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="containerName"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<string>> LCSubDeviceReset(int instrumentid, string subDeviceName)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/SubDevice/Volume/Reset?cdsId={instrumentid}&subDeviceName={subDeviceName}";
try
{
return await httpClient.PutAsync<string>(requestUrl, null);
}
catch (HttpRequestException ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 重置进样器
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> InjectorReset(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Injector/Reset?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, null);
}
catch (HttpRequestException ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// LC G7167全部复位
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> HomeAll(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/G7167/HomeAll?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, null);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 重置LC馏分收集器子设备下单某个抽屉
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="subDeviceName"></param>
/// <param name="drawerIndex">抽屉序号从1开始如果传小于等于0的值表示重置整个子设备.对于回收收集器只支持1</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<string>> LCSubDeviceDrawerReset(int instrumentid, string subDeviceName,int drawerIndex)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/SubDevice/Drawer/Volume/Reset?cdsId={instrumentid}&subDeviceName={subDeviceName}&drawerIndex={drawerIndex}";
try
{
return await httpClient.PutAsync<string>(requestUrl, null);
}
catch (Exception ex)
{
// 处理网络异常(如连接失败、超时等)
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC馏分收集器溶剂体积
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="subDeviceName"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<LCContainerVolume>> GetLCContainerVolume(int instrumentid, string subDeviceName)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/SubDevice/Volume?cdsId={instrumentid}&subDeviceName={subDeviceName}";
try
{
return await httpClient.GetAsync<LCContainerVolume>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 获取LC馏分收集器主设备的子模块列表
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<Features>> GetLCFeature(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/SubDevices?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<Features>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC馏分模块抽屉状态
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="deviceType"></param>
/// <param name="deviceSN"></param>
/// <param name="moduleId"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<DrawerState>> GetLCDrawerStatus(int instrumentid, string deviceType,string deviceSN,string moduleId)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/ModuleDrawerStatus?cdsId={instrumentid}&deviceType={deviceType}&deviceSN={deviceSN}&moduleId={moduleId}";
try
{
return await httpClient.GetAsync<DrawerState>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 获取收集器模块的模块类型
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<List<string>>> GetModuleTypes()
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/ModuleTypes";
try
{
return await httpClient.GetAsync<List<string>>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
public async Task<Response<LCG7167DrawerState>> GetLCG7167DrawerState(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/G7167/DrawerState?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<LCG7167DrawerState>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取GC模块常用信息
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<GCModuleInfo>> GetGCModuleInfo(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/GC/Module/Info?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<GCModuleInfo>(requestUrl);
}
catch (Exception ex)
{
// 处理网络异常(如连接失败、超时等)
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC模块常用信息
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<LCModuleInfo>> GetLCModuleInfo(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Module/Info?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<LCModuleInfo>(requestUrl);
}
catch (Exception ex)
{
// 处理网络异常(如连接失败、超时等)
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 取消当前仪器运行的job
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> CancelCurrentJob(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/Action/Cancel/CurrentJob?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, null);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 暂停仪器运行
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> PauseCurrentJob(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/Action/Pause?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, null);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 恢复仪器重新运行
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> ResumeCurrentJob(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/Action/Resume?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, null);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
public async Task<Response<EmptyData>> ShutDownJob(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/Job/Shutdown?cdsId={instrumentid}";
try
{
return await httpClient.PostAsync<EmptyData>(requestUrl, null);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC泵溶剂体积
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<BottleVolumesResponse>> BottleVolume(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/BottleVolume?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<BottleVolumesResponse>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 设置LC泵溶剂体积
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> SetBottleVolume(int instrumentid, BottleVolumesResponse data)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/BottleVolume?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl, data);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC泵准备状态信息
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<purgeState>> PreparationState(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/PreparationState?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<purgeState>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 开始LC泵清洗
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> PumpClean(int instrumentid, purgeCleaningParam data)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/Purge?cdsId={instrumentid}";
try
{
return await httpClient.PostAsync<bool>(requestUrl, data);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 关闭LC泵清洗
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> StopPumpClean(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/Purge?cdsId={instrumentid}";
try
{
return await httpClient.DeleteAsync<bool>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 开始LC泵调节
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="conditionTime"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> PumpCondition(int instrumentid,double conditionTime)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/Condition?cdsId={instrumentid}&conditionTime={conditionTime}";
try
{
return await httpClient.PostAsync<bool>(requestUrl, null);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 关闭LC泵调节
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> StopPumpCondition(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/Condition?cdsId={instrumentid}";
try
{
return await httpClient.DeleteAsync<bool>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 开始LC泵注入
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> PumpPrime(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/Prime?cdsId={instrumentid}";
try
{
return await httpClient.PostAsync<bool>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 关闭LC泵注入
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> StopPumpPrime(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/Prime?cdsId={instrumentid}";
try
{
return await httpClient.DeleteAsync<bool>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC泵废液体积
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<wasteVolume>> WasteBottleVolume(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Pump/WasteBottleVolume?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<wasteVolume>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 读取LC进样器溶剂体积
/// </summary>
/// <param name="instrumentid"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<List<bottleVolume>>> PrepSampleBottleVolume(int instrumentid)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/PrepSampler/BottleVolume?cdsId={instrumentid}";
try
{
return await httpClient.GetAsync<List<bottleVolume>>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 设置LC进样器溶剂体积
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> SetPrepSampleBottleVolume(int instrumentid, List<bottleVolume> data)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/PrepSampler/BottleVolume?cdsId={instrumentid}";
try
{
return await httpClient.PutAsync<bool>(requestUrl,data);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 获取样品盘信息
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="injectorPlateName"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<PlateInfo>> GetPlateExtraInfo(int instrumentid,string injectorPlateName)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Injector/Plate/ExtraInfo?cdsId={instrumentid}&injectorPlateName={injectorPlateName}";
try
{
return await httpClient.GetAsync<PlateInfo>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 替换进样器盘子
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="injectorPlateName"></param>
/// <param name="openLabContainerDisplayName"></param>
/// <param name="keepAnalysisTargets"></param>
/// <param name="replaceFCFlag"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<List<platePlacement>>> PutPlatePlacement(int instrumentid, string injectorPlateName, string openLabContainerDisplayName, bool keepAnalysisTargets, bool replaceFCFlag)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Injector/Plate/Replacement?cdsId={instrumentid}&injectorPlateName={injectorPlateName}&keepAnalysisTargets={keepAnalysisTargets}&replaceFCFlag={replaceFCFlag}";
try
{
return await httpClient.PutAsync<List<platePlacement>>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 替换馏分收集器盘子
/// </summary>
/// <param name="instrumentid"></param>
/// <param name="fractionCollectorPlateName"></param>
/// <param name="openLabContainerDisplayName"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<List<platePlacement>>> PutLCPlatePlacement(int instrumentid, string fractionCollectorPlateName, string openLabContainerDisplayName)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/FractionCollector/Plate/Replacement?cdsId={instrumentid}&fractionCollectorPlateName={fractionCollectorPlateName}&openLabContainerDisplayName={openLabContainerDisplayName}";
try
{
return await httpClient.PutAsync<List<platePlacement>>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 开启/关闭模块相关启用状态
/// </summary>
/// <param name="instrumentid">仪器Id</param>
/// <param name="moduleIdentity">仪器模块Id</param>
/// <param name="state">要设置的状态 0: Standby/off 1: on</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<bool>> LCModuleState(int instrumentid, string moduleIdentity, int state)
{
var requestUrl = $"{_baseUrl}/api/v1/Instrument/LC/Module/State?cdsId={instrumentid}&moduleIdentity={moduleIdentity}&state={state}";
try
{
return await httpClient.PutAsync<bool>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
#endregion
#region Sample
/// <summary>
/// 按批次设置Sample数据
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<int>> Sample(List<limsSamples> data)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/batch";
try
{
return await httpClient.PostAsync<int>(requestUrl,data);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 增加一个新样品
/// </summary>
/// <param name="sample"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<int>> AddOneSample(limsSamples sample)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample";
try
{
return await httpClient.PostAsync<int>(requestUrl, sample);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 根据id删除大样品数据
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<int>> DeleteBigSample(string id)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample?id={id}";
try
{
return await httpClient.DeleteAsync<int>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 根据id获取Sample数据
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<limsSamples>> GetSampleById(string id)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/id?id={id}";
try
{
return await httpClient.DeleteAsync<limsSamples>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 根据id更新Sample数据
/// </summary>
/// <param name="id"></param>
/// <param name="sample"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<int>> RefreshSampleById(string id,limsSamples sample)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/id?id={id}";
try
{
return await httpClient.PutAsync<int>(requestUrl, sample);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 根据barcode查询大样品数据
/// </summary>
/// <param name="barcode"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<limsSamples>> ReserchSampleByBarcode(string barcode)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/barcode?barcode={barcode}";
try
{
return await httpClient.GetAsync<limsSamples>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
public async Task<Response<limsSamples>> RefreshSampleByBarcode(string barcode)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/barcode?barcode={barcode}";
try
{
return await httpClient.PutAsync<limsSamples>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
public async Task<Response<int>> DeleteSampleByBarcode(string barcode)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/barcode?barcode={barcode}";
try
{
return await httpClient.DeleteAsync<int>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 根据BatchId获取Sample数据
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<limsSamples>> GetSampleByBatchId(string batchId)
{
var requestUrl = $"{_baseUrl}/api/v1/Sample/batchId?batchId={batchId}";
try
{
return await httpClient.DeleteAsync<limsSamples>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
#endregion
#region SampleTestItem
public async Task<Response<SampleItem>> GetSampleItemByBarcode(string barcode)
{
var requestUrl = $"{_baseUrl}/api/v1/SampleTestItem/barcode?barcode={barcode}";
try
{
return await httpClient.GetAsync<SampleItem>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
public async Task<Response<int>> RefreshSampleItemByBarcode(string barcode,SampleItem sampleItem)
{
var requestUrl = $"{_baseUrl}/api/v1/SampleTestItem/barcode?barcode={barcode}";
try
{
return await httpClient.PutAsync<int>(requestUrl,sampleItem);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
public async Task<Response<int>> DeleteSampleItemByBarcode(string barcode)
{
var requestUrl = $"{_baseUrl}/api/v1/SampleTestItem/barcode?barcode={barcode}";
try
{
return await httpClient.DeleteAsync<int>(requestUrl);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
/// <summary>
/// 增加测试瓶数据
/// </summary>
/// <param name="sampleBarcode"></param>
/// <param name="sampleItem"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<Response<int>> AddSampleItemByBarcode(string sampleBarcode,SampleItem sampleItem)
{
var requestUrl = $"{_baseUrl}/api/v1/SampleTestItem?sampleBarcode={sampleBarcode}";
try
{
return await httpClient.PostAsync<int>(requestUrl,sampleItem);
}
catch (Exception ex)
{
throw new Exception($"{ex.Message}", ex);
}
}
#endregion
#region Store
#endregion
#region SystemInfo
#endregion
#region WorkPlan
#endregion
#region InstrumentAnalysisRun
#endregion
#region EventHandler
#endregion
}
}

View File

@ -0,0 +1,138 @@
using MegaRobo.Contract;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class Response<T>
{
public int statusCode { get; set; }
public string errorCode { get; set; }
public string errorMessage { get; set; }
public string message { get; set; }
public T data { get; set; }
}
public class HttpOperateHelper : IDisposable
{
private readonly HttpClient httpClient;
private bool _disposed = false;
public HttpOperateHelper()
{
// 初始化HttpClient设置默认超时和请求头
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.Timeout = TimeSpan.FromSeconds(30); // 设置超时时间
}
// 通用的POST请求方法适用于[HttpPost]接口)
public async Task<Response<T?>> PostAsync<T>(string url, object data = null)
{
try
{
string jsonData = data != null ? JsonConvert.SerializeObject(data) : "{}";
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(url, content);
response.EnsureSuccessStatusCode(); // 抛出HTTP错误状态码异常
string responseJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response<T>>(responseJson);
}
catch (Exception ex)
{
return new Response<T>
{
statusCode = 1,
errorMessage = $"请求失败:{ex.Message}"
};
}
}
// 通用的PUT请求方法适用于[HttpPut]接口)
public async Task<Response<T>> PutAsync<T>(string url, object data = null)
{
try
{
string jsonData = data != null ? JsonConvert.SerializeObject(data) : "{}";
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var response = await httpClient.PutAsync(url, content);
response.EnsureSuccessStatusCode();
string responseJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response<T>>(responseJson);
}
catch (Exception ex)
{
return new Response<T>
{
statusCode = 1,
errorMessage = $"请求失败:{ex.Message}"
};
}
}
// 通用的DELETE请求方法适用于[HttpDelete]接口)
public async Task<Response<T>> DeleteAsync<T>(string url)
{
try
{
var response = await httpClient.DeleteAsync(url);
response.EnsureSuccessStatusCode();
string responseJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response<T>>(responseJson);
}
catch (Exception ex)
{
return new Response<T>
{
statusCode = 1,
errorMessage = $"请求失败:{ex.Message}"
};
}
}
// 通用的GET请求方法适用于[HttpGet]接口)
public async Task<Response<T>> GetAsync<T>(string url)
{
try
{
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string responseJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Response<T>>(responseJson);
}
catch (Exception ex)
{
return new Response<T>
{
statusCode = 1,
errorMessage = $"请求失败:{ex.Message}"
};
}
}
// 释放资源实现IDisposable接口
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
httpClient?.Dispose(); // 释放HttpClient
}
_disposed = true;
}
}
}

View File

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class LoginResponse
{
public string id { get; set; }
public string name { get; set; }
public string account { get; set; }
public string role { get; set; }
public string password { get; set; }
public int pwdSaltFlag { get; set; }
public string authenticationToken { get; set; }
}
public class LCContainerVolume
{
public int tray { get; set; }
public int rack { get; set; }
public int rows { get; set; }
public int cols { get; set; }
public int units { get; set; }
public int max { get; set; }
public List<List<VolumeInfo>> volumes { get; set; }
}
public class VolumeInfo
{
public int max { get; set; } // 最大体积
public int vol { get; set; } // 当前体积
}
public class Features
{
public string name { get; set; }
public string type { get; set; }
public string sn { get; set; }
public List<string> features { get; set; }
}
public class DrawerState
{
public string type { get; set; }
public string sn { get; set; }
public DrawersOpen drawersOpen { get; set; }
}
public class DrawersOpen
{
public bool additionalProp1 { get; set; }
public bool additionalProp2 { get; set; }
public bool additionalProp3 { get; set; }
}
public class LCG7167DrawerState
{
public string drawer { get; set; }
public bool drawerOpen { get; set; }
public int plateStateFront { get; set; }
public int plateStateBack { get; set; }
}
public class GCModuleInfo
{
public int doorOpen { get; set; }
public int methodRuntime { get; set; }
public int runTime { get; set; }
public int methodPostTime { get; set; }
public int postTime { get; set; }
public int status { get; set; }
public int ready { get; set; }
public List<string> notReadyInfo { get; set; }
public List<string> errorInfo { get; set; }
public int hostReady { get; set; }
public int connectionState { get; set; }
public string alsRunStatus { get; set; }
public AlsFront alsFront { get; set; }
public AlsBack alsBack { get; set; }
public AlsTray alsTray { get; set; }
}
public class AlsFront
{
public int capacity { get; set; }
public string model { get; set; }
public int doorOpen { get; set; }
public string status { get; set; }
public state state { get; set; }
}
public class AlsBack
{
public int capacity { get; set; }
public string model { get; set; }
public int doorOpen { get; set; }
public string status { get; set; }
public state state { get; set; }
}
public class AlsTray
{
public int capacity { get; set; }
public string model { get; set; }
public int doorOpen { get; set; }
public string status { get; set; }
public state state { get; set; }
}
public class state
{
public int key { get; set; }
public string description { get; set; }
}
public class LCModuleInfo
{
List<LCModule> modules { get; set; } // 模块列表
}
public class LCModule
{
public string moduleId { get; set; } // 模块ID
public string moduleName { get; set; } // 模块名称
public state emfSummary { get; set; } // EMF摘要状态
public state error { get; set; } // 错误状态
public state notReady { get; set; } // 未就绪状态
public state connection { get; set; } // 连接状态
public long postTime { get; set; } // 发布时间(时间戳)
public long methodPostTime { get; set; } // 方法发布时间(时间戳)
public long runtime { get; set; } // 运行时间(可能是毫秒)
public long methodRuntime { get; set; } // 方法运行时间(可能是毫秒)
public string runState { get; set; } // 运行状态(如"Running"、"Stopped"
}
public class EmptyData
{
// 无需任何属性仅作为占位符匹配JSON中的空对象
}
public class BottleVolumesResponse
{
public List<bottleVolume> bottleVolumes { get; set; }
}
public class bottleVolume
{
public string name { get; set; }
public int current { get; set; }
public int maximum { get; set; }
}
public class compose
{
public int additionalProp1 { get; set; }
public int additionalProp2 { get; set; }
public int additionalProp3 { get; set; }
}
public class purgeParameters
{
public int duration { get; set; }
public int flow { get; set; }
public compose drawersOpen { get; set; }
}
public class purgeState
{
public bool isPurging { get; set; }
public int remainingPurgeTime { get; set; }
public purgeParameters purgeParameters { get; set; }
public bool isConditioning { get; set; }
public int remainingConditionTime { get; set; }
public int conditionTime { get; set; }
public bool isPriming { get; set; }
public int elapsedPrimeTime { get; set; }
}
public class purgeCleaningParam
{
public int duration { get; set; }
public int flow { get; set; }
public compose compositions { get; set; }
}
public class wasteVolume
{
public int current { get; set; }
public int maximum { get; set; }
}
public class PlateInfo
{
public string id { get; set; }
public string displayName { get; set; }
public bool isPlate { get; set; }
public int numLocation { get; set; }
[JsonPropertyName("readonly")]
public bool rreadonly { get; set; }
public string xml { get; set; }
public int numRows { get; set; }
public int numCols { get; set; }
public int isLinear { get; set; }
public int rowStart { get; set; }
public int colStart { get; set; }
public int start { get; set; }
public string original1 { get; set; }
}
public class platePlacement
{
public string moduleId { get; set; }
public string moduleName { get; set; }
public string state { get; set; }
public string summary { get; set; }
}
public class limsSamples
{
public string id { get; set; }
public string barCode { get; set; }
public string type { get; set; }
public string collectDate { get; set; }
public string name { get; set; }
public string equipment { get; set; }
public string sampleSite { get; set; }
public string status { get; set; }
public string batchId { get; set; }
public string addDateTime { get; set; }
public List<viaParam> vialList { get; set; }
}
public class viaParam
{
public string id { get; set; }
public string type { get; set; }
public string barCode { get; set; }
public string sampleBarCode { get; set; }
public string sampleId { get; set; }
public string labName { get; set; }
public string analysisProject { get; set; }
public string analysisMethod { get; set; }
public int analysisTimeInSecond { get; set; }
public string testCode { get; set; }
public string status { get; set; }
public int priority { get; set; }
public string addDateTime { get; set; }
public string placeOnInstrumentDateTime { get; set; }
public int detReportFlag { get; set; }
public string limsId1 { get; set; }
public string limsId2 { get; set; }
public string limsId3 { get; set; }
public string desription { get; set; }
}
public class SampleItem
{
public string id { get; set; }
public string type { get; set; }
public string barCode { get; set; }
public string sampleBarCode { get; set; }
public string sampleId { get; set; }
public string labName { get; set; }
public string analysisProject { get; set; }
public string analysisMethod { get; set; }
public int analysisTimeInSecond { get; set; }
public string testCode { get; set; }
public string status { get; set; }
public int priority { get; set; }
public string addDateTime { get; set; }
public string placeOnInstrumentDateTime { get; set; }
public int detReportFlag { get; set; }
public string limsId1 { get; set; }
public string limsId2 { get; set; }
public string limsId3 { get; set; }
public string desription { get; set; }
}
}

View File

@ -0,0 +1,305 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MegaRobo.Connections;
using MegaRobo.Connections.Web;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
namespace MegaRobo.C00225155.ControlDevices;
/// <summary>
/// http://192.168.50.20:8888/swagger/index.html
/// </summary>
public sealed class AgvService : ConnectorBase, IConnection, IDisposable, IConnectionAsync
{
public AgvService()
{
}
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
this.CoreService = coreService;
try
{
if(args is [string hostname, ..])
{
this._hostname = hostname.Trim('/');
this._httpService = new HttpClientEx { StringEncoder = Encoding.UTF8, BaseAddress = new Uri(this._hostname) };
this._httpService.Timeout = TimeSpan.FromSeconds(5);
}
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
await Task.Delay(0);
return string.Empty;
}
private string _hostname = "";
private HttpClientEx _httpService;
public decimal ChargeVolume { get; set; } // 电量 0-1 即为 100%为满电
public string CurrentStationName { get; set; } = string.Empty;
public bool IsInChargeLocation => false;
public bool IsInWaitLocation => false;
public string ChargeLocationCode { get; set; }
public string WaitLocationCode { get; set; }
public string TargetLocation = "";
public override async Task<ConnectionState> CheckConnected()
{
await Task.Delay(0);
return ConnectionState.IsConnected;
}
public override async Task<ConnectionState> ReconnectAsync()
{
await Task.Delay(0);
return ConnectionState.IsConnected;
}
#region Status
public async Task<bool> CheckMoveDone()
{
var currStation= await this.ReadCurrentStation();
if(string.IsNullOrWhiteSpace(currStation) || string.IsNullOrWhiteSpace(this.TargetLocation))
{
return false;
}
return string.Equals(currStation, this.TargetLocation, StringComparison.CurrentCultureIgnoreCase);
}
public async Task<bool> CheckMoveing()
{
try
{
this.CurrentStationName = await this.ReadCurrentStation();
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
return false;
}
#endregion Status
#region Init
public async void Comfirmloc()
{
try
{
string apiName = "/RobotControl/Comfirmloc";
var response = await this._httpService.PostJsonAsync(apiName, string.Empty);
this.Logger.LogDebug(response);
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
}
public async void Lock(string name = "mega")
{
try
{
string apiName = this._hostname + "/RobotConfig/Lock";
// var args = new { nick_name = name };
//var response = await this._httpService.PostJsonAsync(apiName, args.ToJson());
IDictionary<string, string> argdict = new Dictionary<string, string>();
argdict.Add("nick_name", name);
apiName = this._httpService.UrlCombine(apiName, argdict);
var response = await this._httpService.PostJsonAsync(apiName, string.Empty);
this.Logger.LogInformation(response);
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
}
public async void Unlock()
{
try
{
string apiName = "/RobotConfig/Unlock";
var response = await this._httpService.PutAsync(apiName, null);
if(response.IsSuccessStatusCode)
{
this.Logger.LogInformation($"释放控制权");
}
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
}
#endregion Init
#region
/// <summary>
/// AGV电量
/// </summary>
/// <returns></returns>
public async Task<string> ReadBattery()
{
try
{
string apiName = this._hostname + "/RobotStatus/Battery";
IDictionary<string, string> argdict = new Dictionary<string, string>();
argdict.Add("simple", false.ToString());
apiName = this._httpService.UrlCombine(apiName, argdict);
var response = await this._httpService.GetStringAsync(apiName);
var batteryLevel = response.GetJsonValueByToken<string>("data.battery_level");
return batteryLevel;
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
return null;
}
public async Task<string> ReadCurrentStation()
{
try
{
string apiName = this._hostname + "/RobotStatus/Loc";
IDictionary<string, string> argDict = new Dictionary<string, string>();
argDict.Add("simple", false.ToString());
apiName = this._httpService.UrlCombine(apiName, argDict);
var response = await this._httpService.GetStringAsync(apiName);
var currentStation = response.GetJsonValueByToken<string>("data.current_station");
return currentStation;
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
return null;
}
public async Task<bool> ReadTaskStatus()
{
try
{
//https://support.seer-group.com/d/1674676593139699713.html
string apiName = this._hostname + "/RobotStatus/Task";
// IDictionary<string, string> argdict = new Dictionary<string, string>();
// argdict.Add("simple", false.ToString());
apiName = this._httpService.UrlCombine(apiName, null);
var response = await this._httpService.GetStringAsync(apiName);
var taskStatus = response.GetJsonValueByToken<string>("data.task_status");
return new[] { "2", "3" }.Contains(taskStatus);
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
return false;
}
public async Task ClearAllErrors()
{
try
{
string apiName = this._hostname + "/RobotConfig/Clearallerrors";
// IDictionary<string, string> argdict = new Dictionary<string, string>();
// argdict.Add("simple", false.ToString());
apiName = this._httpService.UrlCombine(apiName, null);
var response = await this._httpService.GetStringAsync(apiName);
response.GetJsonValueByToken<string>("data.ret_code");
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
}
#endregion
#region Move
private async Task<string> LoadCurrentLocationAsync()
{
try
{
string apiName = this._hostname + "/RobotStatus/Loc";
var response = await this._httpService.GetStringAsync(apiName);
var currentStation = response.GetJsonValueByToken<string>("data.current_station");
return currentStation;
} catch(Exception ex)
{
Console.WriteLine(ex);
}
return null;
}
public async void MoveToLocation(string location)
{
try
{
this.TargetLocation = location;
this.Comfirmloc();
string apiName = this._hostname + "/RobotTask/Gotarget";
string sourceId = "SELF_POSITION";
// sourceId = await this.LoadCurrentLocationAsync();
var locationItem = new { id = location, source_id = sourceId, task_id = $"goto_{DateTime.Now:yyyyMMdd_HHmmssfff}_{location}" };
var jsonRaw = locationItem.ToJson(true);
var response = await this._httpService.PostJsonAsync(apiName, jsonRaw);
this.Logger.LogInformation(response);
} catch(Exception ex)
{
Console.WriteLine(ex);
}
}
/// <summary>
/// 指定路过某点到某点
/// 优点: 会按照任务发送队列执行
/// 缺点: 假如同命令连续发送多次,可能会造成首次命令执行后原地掉头导致碰撞风险;
/// </summary>
/// <param name="locations"></param>
public async void MoveToLocation(IList<string> locations)
{
if(locations == null || !locations.Any())
return;
try
{
this.Comfirmloc();
string apiName = this._hostname + "/RobotTask/Gotargetlist";
string sourceId = "SELF_POSITION";
sourceId = await this.LoadCurrentLocationAsync();
IList<object> orderList = new List<object>();
for(var index = 0; index < locations.Count; index++)
{
string item = locations[index];
if(index > 0)
{
sourceId = locations[index - 1];
}
var locationItem = new { id = item, source_id = sourceId, task_id = $"goto_{DateTime.Now:yyyyMMdd_HHmmssfff}_{item}" };
orderList.Add(locationItem);
}
var args = new { move_task_list = orderList };
var jsonRaw = args.ToJson(true);
var response = await this._httpService.PostJsonAsync(apiName, jsonRaw);
this.Logger.LogInformation(response);
} catch(Exception ex)
{
Console.WriteLine(ex);
}
}
#endregion Move
#region Watcher
#endregion Watcher
}

View File

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MegaRobo.Connections;
using MegaRobo.Contract;
using MegaRobo.Contract.Abstractions;
using MegaRobo.Logger;
namespace MegaRobo.C00225155.ControlDevices;
public class CameraLightService : DeviceBase, IConnection, IDisposable, IConnectionAsync
{
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
await Task.Delay(0);
try
{
string portName = "COM1";
if(args is [string portIndex, ..])
{
portName = portIndex;
}
this.CoreService = coreService;
this.SerialPortManger = new SerialPortManger();
bool isSuccess = this.SerialPortManger.Open(portName);
this.SerialPortManger.SetPort(portName, 19200, DataBits.Eight, StopBits.One, Parity.None);
// bool isSuccess = this.SerialPortManger.Open();
// int retCode = ControllerDllCSharp.ClassLibControllerDll.CreateSerialPort(3, ref this._controllerHandle); //创建串口
this.Logger.LogDebug($"打开串口Com3{(isSuccess ? "" : "")}");
} catch(Exception ex)
{
this.Logger.LogException(ex);
}
return string.Empty;
}
#region properties
public SerialPortManger SerialPortManger { get; set; }
public ConnectionState ConnectionState { get; set; }
public event EventHandler<ConnectionState> ConnectChanged;
public TimeSpan Timeout { get; set; }
public bool IsConnected => SerialPortManger?.IsOpen ?? false;
#endregion properties
#region functions
public async Task<ConnectionState> CheckConnected()
{
await Task.Delay(0);
return ConnectionState.IsConnected;
}
public async Task<ConnectionState> ReconnectAsync()
{
await Task.Delay(0);
return ConnectionState.IsConnected;
}
public async Task SetValue(byte value, params int[] channelIndexs)
{
if(channelIndexs.Length == 0)
channelIndexs = new[] { 1, 2, 3, 4 };
var channelChars = new[] { "0", "A", "B", "C", "D" };
List<string> cmds = channelIndexs.Select(index => $"S{channelChars[index]}{value:D4}#").ToList();
string cmdLine = string.Join("", cmds);
var code = await this.SerialPortManger.WriteAndGetReplyAsync(cmdLine, TimeSpan.FromSeconds(1.5));
this.Logger.LogDebug($"设置光源cmd:{cmds} result:{code}");
}
public void Close()
{
this.SerialPortManger.Close();
}
public void Dispose()
{
// TODO release managed resources here
}
#endregion functions
#region events
#endregion events
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MegaRobo.Connections;
using MegaRobo.Connections.Modbus;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
namespace MegaRobo.C00225155.ControlDevices
{
public class CameraTcpClientService : ConnectorBase<MegaTcpClient>
{
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
this.CoreService = coreService;
try
{
base.CoreService = coreService;
if (args is [IPEndPoint remoteHost, ..])
{
base.ConnectorAdapter = new MegaTcpClient
{
Logger = coreService.Logger,
Name = base.Name
};
if (base.ConnectorAdapter != null)
{
base.ConnectorAdapter.DataReceived += ConnectorOnDataReceived;
}
ConnectionState = await base.ConnectorAdapter.ConnectAsync(remoteHost);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
return await Task.FromResult(ex.Message);
}
return string.Empty;
}
private void ConnectorOnDataReceived(object sender, TcpMessage tcpMsg)
{
if (tcpMsg is null)
return;
var msg = tcpMsg.ParseStirng();
if (string.IsNullOrEmpty(msg)) return;
this.Logger.LogInformation($"接收到配置站相机_{this.Name}数据:{msg?.Trim()}");
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
/// <summary>
/// 触发相机拍照
/// </summary>
/// <param name="timeout"></param>
/// <param name="runSwitch"></param>
/// <returns></returns>
public async Task<bool> TriggerCamera(string sn, string bottleType, TimeSpan timeout)
{
try
{
string command = $"S,{sn},{bottleType},Start,T";
var tcpMessage =await ConnectorAdapter.WriteAndGetReplyAsync(command, timeout);
if (tcpMessage is null)
return false;
if (tcpMessage.ParseStirng() == "S,End,T") return true;
else return false;
}
catch (Exception ex)
{
base.Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService", "Reset", 150, false);
return false;
}
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class CentreControlService
{
}
}

View File

@ -0,0 +1,130 @@
{
"ExtendedData": {
"inputs": [
"http://192.168.5.11:51156/LabX/SystemIntegrationService/mex"
],
"collectionTypes": [
"System.Array",
"System.Collections.Generic.Dictionary`2"
],
"namespaceMappings": [
"*, ServiceReference1"
],
"references": [
"CommunityToolkit.Mvvm, {CommunityToolkit.Mvvm, 8.4.0}",
"CsvHelper, {CsvHelper, 33.1.0}",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\Common.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.C00225155.DataAccess.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.C00225155.Entities.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.Connections.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.Contract.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.ControlDevices.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.DataAccess.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.Entities.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.Logger.dll",
"D:\\C00225155_20260319\\C00225155\\C00225155\\MegaRobo.C00225155\\MegaRobo.C00225155.ControlDevices\\bin\\Debug\\net7.0-windows\\MegaRobo.Utility.dll",
"DmProvider, {SqlSugarCore.Dm, 1.2.0}",
"Kdbndp, {SqlSugarCore.Kdbndp, 7.4.0}",
"LightInject, {LightInject, 6.6.4}",
"Microsoft.Data.SqlClient, {Microsoft.Data.SqlClient, 2.1.4}",
"Microsoft.Data.Sqlite, {Microsoft.Data.Sqlite.Core, 7.0.5}",
"Microsoft.Extensions.ObjectPool, {Microsoft.Extensions.ObjectPool, 6.0.16}",
"Microsoft.IdentityModel.JsonWebTokens, {Microsoft.IdentityModel.JsonWebTokens, 6.8.0}",
"Microsoft.IdentityModel.Logging, {Microsoft.IdentityModel.Logging, 6.8.0}",
"Microsoft.IdentityModel.Protocols, {Microsoft.IdentityModel.Protocols, 6.8.0}",
"Microsoft.IdentityModel.Protocols.OpenIdConnect, {Microsoft.IdentityModel.Protocols.OpenIdConnect, 6.8.0}",
"Microsoft.IdentityModel.Protocols.WsTrust, {Microsoft.IdentityModel.Protocols.WsTrust, 6.8.0}",
"Microsoft.IdentityModel.Tokens, {Microsoft.IdentityModel.Tokens, 6.8.0}",
"Microsoft.IdentityModel.Tokens.Saml, {Microsoft.IdentityModel.Tokens.Saml, 6.8.0}",
"Microsoft.IdentityModel.Xml, {Microsoft.IdentityModel.Xml, 6.8.0}",
"Microsoft.Win32.Primitives, {Microsoft.Win32.Primitives, 4.3.0}",
"Microsoft.Win32.SystemEvents, {Microsoft.Win32.SystemEvents, 6.0.0}",
"Modbus, {NModbus4.NetCore, 3.0.0}",
"MySqlConnector, {MySqlConnector, 2.2.5}",
"NETStandard.HttpListener, {NETStandard.HttpListener, 1.0.3.5}",
"Newtonsoft.Json, {Newtonsoft.Json, 13.0.3}",
"Npgsql, {Npgsql, 5.0.7}",
"Oracle.ManagedDataAccess, {Oracle.ManagedDataAccess.Core, 3.21.100}",
"SQLitePCLRaw.batteries_v2, {SQLitePCLRaw.bundle_e_sqlite3, 2.1.4}",
"SQLitePCLRaw.core, {SQLitePCLRaw.core, 2.1.4}",
"SQLitePCLRaw.provider.e_sqlite3, {SQLitePCLRaw.provider.e_sqlite3, 2.1.4}",
"SqlSugar, {SqlSugarCore, 5.1.4.110}",
"System.AppContext, {System.AppContext, 4.3.0}",
"System.CodeDom, {System.CodeDom, 8.0.0}",
"System.Collections, {System.Collections, 4.3.0}",
"System.Collections.Concurrent, {System.Collections.Concurrent, 4.3.0}",
"System.ComponentModel.Annotations, {System.ComponentModel.Annotations, 5.0.0}",
"System.Configuration.ConfigurationManager, {System.Configuration.ConfigurationManager, 6.0.0}",
"System.Console, {System.Console, 4.3.0}",
"System.Data.Common, {System.Data.Common, 4.3.0}",
"System.Diagnostics.Debug, {System.Diagnostics.Debug, 4.3.0}",
"System.Diagnostics.PerformanceCounter, {System.Diagnostics.PerformanceCounter, 6.0.1}",
"System.Diagnostics.Tools, {System.Diagnostics.Tools, 4.3.0}",
"System.Diagnostics.Tracing, {System.Diagnostics.Tracing, 4.3.0}",
"System.DirectoryServices, {System.DirectoryServices, 6.0.1}",
"System.DirectoryServices.Protocols, {System.DirectoryServices.Protocols, 6.0.1}",
"System.Drawing.Common, {System.Drawing.Common, 6.0.0}",
"System.Formats.Asn1, {System.Formats.Asn1, 6.0.0}",
"System.Globalization, {System.Globalization, 4.3.0}",
"System.Globalization.Calendars, {System.Globalization.Calendars, 4.3.0}",
"System.IdentityModel.Tokens.Jwt, {System.IdentityModel.Tokens.Jwt, 6.8.0}",
"System.IO, {System.IO, 4.3.0}",
"System.IO.Compression, {System.IO.Compression, 4.3.0}",
"System.IO.Compression.ZipFile, {System.IO.Compression.ZipFile, 4.3.0}",
"System.IO.FileSystem, {System.IO.FileSystem, 4.3.0}",
"System.IO.FileSystem.Primitives, {System.IO.FileSystem.Primitives, 4.3.0}",
"System.IO.Ports, {System.IO.Ports, 7.0.0}",
"System.Linq, {System.Linq, 4.3.0}",
"System.Linq.Expressions, {System.Linq.Expressions, 4.3.0}",
"System.Management, {System.Management, 8.0.0}",
"System.Net.Http, {System.Net.Http, 4.3.0}",
"System.Net.Primitives, {System.Net.Primitives, 4.3.0}",
"System.Net.Sockets, {System.Net.Sockets, 4.3.0}",
"System.ObjectModel, {System.ObjectModel, 4.3.0}",
"System.Reflection, {System.Reflection, 4.3.0}",
"System.Reflection.DispatchProxy, {System.Reflection.DispatchProxy, 4.7.1}",
"System.Reflection.Emit.ILGeneration, {System.Reflection.Emit.ILGeneration, 4.3.0}",
"System.Reflection.Emit.Lightweight, {System.Reflection.Emit.Lightweight, 4.3.0}",
"System.Reflection.Extensions, {System.Reflection.Extensions, 4.3.0}",
"System.Reflection.Primitives, {System.Reflection.Primitives, 4.3.0}",
"System.Resources.ResourceManager, {System.Resources.ResourceManager, 4.3.0}",
"System.Runtime, {System.Runtime, 4.3.0}",
"System.Runtime.Extensions, {System.Runtime.Extensions, 4.3.0}",
"System.Runtime.Handles, {System.Runtime.Handles, 4.3.0}",
"System.Runtime.InteropServices, {System.Runtime.InteropServices, 4.3.0}",
"System.Runtime.InteropServices.RuntimeInformation, {System.Runtime.InteropServices.RuntimeInformation, 4.3.0}",
"System.Runtime.Numerics, {System.Runtime.Numerics, 4.3.0}",
"System.Security.AccessControl, {System.Security.AccessControl, 6.0.0}",
"System.Security.Cryptography.Algorithms, {System.Security.Cryptography.Algorithms, 4.3.0}",
"System.Security.Cryptography.Cng, {System.Security.Cryptography.Cng, 4.5.0}",
"System.Security.Cryptography.Encoding, {System.Security.Cryptography.Encoding, 4.3.0}",
"System.Security.Cryptography.Pkcs, {System.Security.Cryptography.Pkcs, 6.0.1}",
"System.Security.Cryptography.Primitives, {System.Security.Cryptography.Primitives, 4.3.0}",
"System.Security.Cryptography.ProtectedData, {System.Security.Cryptography.ProtectedData, 6.0.0}",
"System.Security.Cryptography.X509Certificates, {System.Security.Cryptography.X509Certificates, 4.3.0}",
"System.Security.Cryptography.Xml, {System.Security.Cryptography.Xml, 6.0.1}",
"System.Security.Permissions, {System.Security.Permissions, 6.0.0}",
"System.Security.Principal.Windows, {System.Security.Principal.Windows, 5.0.0}",
"System.ServiceModel.Duplex, {System.ServiceModel.Duplex, 6.0.0}",
"System.ServiceModel.Federation, {System.ServiceModel.Federation, 6.0.0}",
"System.ServiceModel.Http, {System.ServiceModel.Http, 6.0.0}",
"System.ServiceModel.NetFramingBase, {System.ServiceModel.NetFramingBase, 6.0.0}",
"System.ServiceModel.NetTcp, {System.ServiceModel.NetTcp, 6.0.0}",
"System.ServiceModel.Primitives, {System.ServiceModel.Primitives, 6.0.0}",
"System.ServiceModel.Security, {System.ServiceModel.Security, 6.0.0}",
"System.Text.Encoding, {System.Text.Encoding, 4.3.0}",
"System.Text.Encoding.CodePages, {System.Text.Encoding.CodePages, 5.0.0}",
"System.Text.Encoding.Extensions, {System.Text.Encoding.Extensions, 4.3.0}",
"System.Text.RegularExpressions, {System.Text.RegularExpressions, 4.3.0}",
"System.Threading, {System.Threading, 4.3.0}",
"System.Threading.Tasks, {System.Threading.Tasks, 4.3.0}",
"System.Threading.Timer, {System.Threading.Timer, 4.3.0}",
"System.Windows.Extensions, {System.Windows.Extensions, 6.0.0}",
"System.Xml.ReaderWriter, {System.Xml.ReaderWriter, 4.3.0}",
"System.Xml.XDocument, {System.Xml.XDocument, 4.3.0}",
"System.Xml.XmlDocument, {System.Xml.XmlDocument, 4.3.0}"
],
"targetFramework": "net7.0-windows",
"typeReuseMode": "All"
}
}

View File

@ -0,0 +1,45 @@
using MegaRobo.Connections.Web;
using MegaRobo.Logger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class HttpListenerProvider
{
public HttpListenerProvider(ILogger logger)
{
this._Logger = logger;
}
private ILogger _Logger;
private readonly IDictionary<string, HttpListenerService> _sources = new ConcurrentDictionary<string, HttpListenerService>(StringComparer.CurrentCultureIgnoreCase);
public HttpListenerService GetOrAdd(string name, IPEndPoint host)
{
return this._sources.TryGetValue(name, out HttpListenerService service) ? service : this.Create(name, host);
}
public HttpListenerService Create(string name, IPEndPoint host)
{
var service = new HttpListenerService();
service.Start(host);
if (this._sources.ContainsKey(name))
{
this._sources[name] = service;
}
else
{
this._sources.TryAdd(name, service);
}
this._Logger?.LogInformation($"已开启端口为[{host}]的Web请求监听");
return service;
}
}
}

View File

@ -0,0 +1,14 @@
using MegaRobo.ControlDevices.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public sealed class HuichuanAc801Service : ModbusBase
{
}
}

View File

@ -0,0 +1,301 @@
using CommunityToolkit.Mvvm.Messaging;
using CsvHelper;
using CsvHelper.Configuration;
using Dm;
using MegaRobo.Entities;
using ServiceReference1;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Task = System.Threading.Tasks.Task;
namespace MegaRobo.C00225155.ControlDevices
{
public class LabxService
{
public Guid abortWaitingHeadTaskId = new Guid();
public BasicSystemIntegrationServiceClient _serviceClient;
public List<Instrument> Instruments { get; set; }
public Dictionary<Guid, Instrument> CurTaskID { get; set; }
public LabxService()
{
_serviceClient = new BasicSystemIntegrationServiceClient();
}
public async Task<List<Instrument>> GetInstruments(string instrumentName)
{
try
{
var instrument = await _serviceClient.GetThisInstrumentsByNameAsync(instrumentName);
return instrument.ToList();
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"labx获取设备发生异常\r\n请联系供应商",
Content = $"{ex.ToString()}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
return null;
}
}
public async void CreateNewTask(string methodName, string taskName)
{
//Get the method based on method user ID
Method method = await _serviceClient.GetThisMethodByUserDefinedIdAsync(methodName);
var taskId = _serviceClient.CreateTaskAsync(
method.BusinessId, taskName, false, new TaskParameterSet());
}
public async Task<TaskResultSet> CreateAndStartNewTaskMove(string methodName, string taskName, int holeNum, Instrument instrument)
{
Method method = await _serviceClient.GetThisMethodByUserDefinedIdAsync(methodName);
var methodparameter = await _serviceClient.GetMethodMetaDataAsync(method.BusinessId);
TaskParameterSet taskParameterSet = new TaskParameterSet();
IntegerTypeMethodParameter parameter = new IntegerTypeMethodParameter();
parameter.MethodFunctionName = methodparameter.MethodParameters[0].MethodFunctionName;
parameter.MethodFunctionPropertyName = methodparameter.MethodParameters[0].MethodFunctionPropertyName;
parameter.Value = holeNum;
taskParameterSet.GlobalMethodParameters = new MethodParameter[1];
taskParameterSet.GlobalMethodParameters[0] = parameter;
Guid taskId = await _serviceClient.CreateTaskAsync(method.BusinessId, taskName, false, taskParameterSet);
await _serviceClient.StartTaskAsync(taskId, instrument.InstrumentId);
//CurTaskID.Add( taskId, instrument );
TaskState taskState;
do
{
taskState = await _serviceClient.GetTaskStateAsync(taskId);
if (taskState.State != TaskStateType.Aborted && taskState.State != TaskStateType.Completed)
{
await Task.Delay(1000);
}
} while (taskState.State != TaskStateType.Completed && taskState.State != TaskStateType.Aborted);
if (taskState.State == TaskStateType.Completed)
{
var result = await _serviceClient.GetTaskResultsAsync(taskId);
//CurTaskID.Remove(taskId);
//var RE= result.Samples[0].Results[0].Scalar.ValueMember;
return result;
}
return null;
}
/// <summary>
/// 等待放置加样头
/// </summary>
/// <param name="instrument"></param>
/// <param name="methodName"></param>
/// <param name="taskName"></param>
/// <param name="defaultvalue"></param>
/// <returns></returns>
public async Task<TaskResultSet> StartWaitingForHead(Instrument instrument, string methodName = "Dosing Head", string taskName = "Dosing Head", decimal defaultvalue = 0)
{
try
{
Method method = await _serviceClient.GetThisMethodByUserDefinedIdAsync(methodName);
var methodparameter = await _serviceClient.GetMethodMetaDataAsync(method.BusinessId);
TaskParameterSet taskParameterSet = new TaskParameterSet
{
GlobalMethodParameters = new MethodParameter[4]
{
new DecimalTypeMethodParameter{MethodFunctionName = "Number1",MethodFunctionPropertyName = "DefaultValue",Value = defaultvalue},
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "TargetWeight",Value = 5000m}, //单位是mg
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "DosingUpperTolerance",Value = 1m},
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "DosingLowerTolerance",Value = 1m}
},
};
Guid taskId = await _serviceClient.CreateTaskAsync(method.BusinessId, taskName, false, taskParameterSet);
abortWaitingHeadTaskId = taskId;
await _serviceClient.StartTaskAsync(taskId, instrument.InstrumentId);
TaskState taskState;
do
{
taskState = await _serviceClient.GetTaskStateAsync(taskId);
if (taskState.State != TaskStateType.Running)
{
await Task.Delay(2000);
}
} while (taskState.State != TaskStateType.Running);
return null;
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"梅特勒粉末研磨模块异常",
Content = $"{ex.ToString()}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
return null;
}
}
/// <summary>
/// 停止等待加样头
/// </summary>
/// <returns></returns>
public async Task AbortWaitingForHead()
{
await _serviceClient.AbortTaskAsync(abortWaitingHeadTaskId);
}
/// <summary>
/// 梅特勒粉末天平模块步骤
/// </summary>
/// <param name="instrument"></param>
/// <param name="methodName"></param>
/// <param name="taskName"></param>
/// <param name="defaultvalue"></param>
/// <param name="targetweight">目标重量单位mg</param>
/// <param name="upT"></param>
/// <param name="dnT"></param>
/// <returns></returns>
public async Task<double> MTPowderModuleControlStep(Instrument instrument, string methodName, string taskName, decimal defaultvalue = 0, decimal targetweight = 0, decimal upT = 0, decimal dnT = 0)
{
double addResult = 0;
try
{
Method method = await _serviceClient.GetThisMethodByUserDefinedIdAsync(methodName);
var methodparameter = await _serviceClient.GetMethodMetaDataAsync(method.BusinessId);
//defaultvalue= 0 关门 =1开门 =2置零 =3去皮 =4 开始加样 =5 粉末头复位
TaskParameterSet taskParameterSet = new TaskParameterSet
{
GlobalMethodParameters = new MethodParameter[4]
{
new DecimalTypeMethodParameter{MethodFunctionName = "Number1",MethodFunctionPropertyName = "DefaultValue",Value = defaultvalue},
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "TargetWeight",Value = targetweight}, //单位是mg
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "DosingUpperTolerance",Value = upT},
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "DosingLowerTolerance",Value = dnT}
},
};
Guid taskId = await _serviceClient.CreateTaskAsync(method.BusinessId, taskName, false, taskParameterSet);
await _serviceClient.StartTaskAsync(taskId, instrument.InstrumentId);
TaskState taskState;
do
{
taskState = await _serviceClient.GetTaskStateAsync(taskId);
if (taskState.State != TaskStateType.Aborted && taskState.State != TaskStateType.Completed)
{
await Task.Delay(2000);
}
} while (taskState.State != TaskStateType.Completed && taskState.State != TaskStateType.Aborted);
if (defaultvalue == 4 && taskState.State == TaskStateType.Completed)
{
var result = await _serviceClient.GetTaskResultsAsync(taskId);
//CurTaskID.Remove(taskId);
decimal RE = result.TaskResults[1].Scalar.ValueMember;
addResult = (double)RE;
return addResult;
}
return addResult;
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"梅特勒粉末研磨模块异常",
Content = $"{ex.ToString()}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
return addResult;
}
}
/// <summary>
/// 加粉控制
/// </summary>
/// <param name="instrument"></param>
/// <param name="methodName"></param>
/// <param name="taskName"></param>
/// <param name="defaultValue"></param>
/// <param name="height"></param>
/// <param name="targetweight"></param>
/// <param name="upT"></param>
/// <param name="dnT"></param>
/// <returns></returns>
public async Task<double> MTPowderModuleAddPowder(Instrument instrument, string methodName, string taskName, decimal defaultValue, decimal height = 140.8m, decimal targetweight = 0, decimal upT = 0, decimal dnT = 0)
{
double addResult = 0;
try
{
Method method = await _serviceClient.GetThisMethodByUserDefinedIdAsync(methodName);
var methodparameter = await _serviceClient.GetMethodMetaDataAsync(method.BusinessId);
//defaultvalue= 0 关门 =1开门 =2置零 =3去皮 =4 开始加样 =5 粉末头复位
TaskParameterSet taskParameterSet = new TaskParameterSet
{
GlobalMethodParameters = new MethodParameter[4]
{
new DecimalTypeMethodParameter{MethodFunctionName = "Dosingsettings1",MethodFunctionPropertyName = "AbsoluteDosingPositionSpan",Value = height},
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "TargetWeight",Value = targetweight}, //单位是mg
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "DosingUpperTolerance",Value = upT},
new DecimalTypeMethodParameter{MethodFunctionName = "Dosing1",MethodFunctionPropertyName = "DosingLowerTolerance",Value = dnT}
},
};
Guid taskId = await _serviceClient.CreateTaskAsync(method.BusinessId, taskName, false, taskParameterSet);
await _serviceClient.StartTaskAsync(taskId, instrument.InstrumentId);
TaskState taskState;
do
{
taskState = await _serviceClient.GetTaskStateAsync(taskId);
if (taskState.State != TaskStateType.Aborted && taskState.State != TaskStateType.Completed)
{
await Task.Delay(2000);
}
} while (taskState.State != TaskStateType.Completed && taskState.State != TaskStateType.Aborted);
if (defaultValue == 4 && taskState.State == TaskStateType.Completed)
{
var result = await _serviceClient.GetTaskResultsAsync(taskId);
try
{
//CurTaskID.Remove(taskId);
decimal RE = result.TaskResults[0].Scalar.ValueMember;
addResult = (double)RE;
}
catch (Exception ex)
{
//MessageBox.Show($"加粉异常-{ex.Message}");
addResult = 0;
}
return addResult;
}
return addResult;
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ConfirmMessage()
{
Header = $"梅特勒粉末研磨模块异常",
Content = $"{ex.ToString()}",
OkButtonContent = "继续",
CancelButtonContent = "取消",
});
return addResult;
}
}
}
}

View File

@ -0,0 +1,68 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\MegaRobo.Common\MegaRobo.Connections\MegaRobo.Connections.csproj" />
<ProjectReference Include="..\..\..\Common\MegaRobo.Common\MegaRobo.ControlDevices\MegaRobo.ControlDevices.csproj" />
<ProjectReference Include="..\MegaRobo.LS60141.Entities\MegaRobo.LS60141.Entities.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="HansRobotLibrarys">
<HintPath>libs\HansRobotLibrarys.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Contract">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Contract.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Entities">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Entities.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Logger">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Logger.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Utility">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Utility.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<Folder Include="JodellTool\" />
</ItemGroup>
<ItemGroup>
<None Update="libs\jodellTool.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\jodellTool.lib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\jodellTool_x64.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\jodellTool_x64.lib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\x64\jodellTool.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\x64\jodellTool.lib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\x86\jodellTool.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="libs\x86\jodellTool.lib">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\MegaRobo.Common\MegaRobo.Connections\MegaRobo.Connections.csproj" />
<ProjectReference Include="..\..\..\Common\MegaRobo.Common\MegaRobo.ControlDevices\MegaRobo.ControlDevices.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\MegaRobo.C00225155.DataAccess\MegaRobo.C00225155.DataAccess.csproj" />
<ProjectReference Include="..\MegaRobo.C00225155.Entities\MegaRobo.C00225155.Entities.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="MegaRobo.Contract">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Contract.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Entities">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Entities.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Logger">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Logger.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Utility">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Utility.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Client" Version="7.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NSwag.ApiDescription.Client" Version="13.18.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="System.ServiceModel.Duplex" Version="6.0.*" />
<PackageReference Include="System.ServiceModel.Federation" Version="6.0.*" />
<PackageReference Include="System.ServiceModel.Http" Version="6.0.*" />
<PackageReference Include="System.ServiceModel.NetTcp" Version="6.0.*" />
<PackageReference Include="System.ServiceModel.Security" Version="6.0.*" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,104 @@
using MegaRobo.Connections.Adapters;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices.Scanner
{
public abstract class ScannerCodeBase<TConnectorAdapter> : ConnectorBase<TConnectorAdapter>, IScannerCode where TConnectorAdapter : ConnectorAdapter
{
public string Code { get; protected set; }
/// <summary>
/// 开始的指令
/// </summary>
protected virtual string StartCommandLine { get; set; } = "Start";
/// <summary>
/// 停止指令
/// </summary>
protected virtual string StopCommandLine { get; set; } = "Stop";
public virtual event EventHandler<string> ScannedEvent;
public event EventHandler<TcpMessage> DataReceived;
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
try
{
this.CoreService = coreService;
switch (args.Length)
{
case >= 1 when args[0] is TConnectorAdapter connector:
this.ConnectorAdapter = connector;
break;
}
await this.ReconnectAsync();
return null;
}
catch (Exception ex)
{
this.Logger.LogException(ex);
return ex.Message;
}
}
public abstract Task<string> ReadAsnyc(TimeSpan timeout);
public virtual void Start()
{
byte[] sendBytes = this.ConnectorAdapter.StringEncoder.GetBytes(this.StartCommandLine);
this.Write(sendBytes);
}
public virtual void Stop()
{
byte[] sendBytes = this.ConnectorAdapter.StringEncoder.GetBytes(this.StopCommandLine);
this.Write(sendBytes);
}
protected virtual void Write(byte[] data)
{
try
{
switch (this.ConnectorAdapter)
{
case MegaTcpClient megaTcpClient:
megaTcpClient.Connector.GetStream().Write(data, 0, data.Length);
break;
// case MegaTcpClientBase tcpClientBase:
// tcpClientBase.Connector.Write(data);
// break;
// case UdpClientBase udpClientBase:
// udpClientBase.Connector.Send(data, data.Length);
// break;
// case SerialPortBase serialPortBase:
// serialPortBase.Connector.Write(data, 0, data.Length);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
protected void OnScannedEvent(string code)
{
this.ScannedEvent?.Invoke(this, code);
}
protected virtual void OnDataReceived(object sender, TcpMessage msg)
{
this.DataReceived?.Invoke(sender, msg);
}
protected abstract string ParseMessage(object data, byte[] sourceData);
}
}

View File

@ -0,0 +1,74 @@
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices.Scanner
{
public abstract class ScannerCodeOfTcpClientBase : ScannerCodeBase<MegaTcpClient>
{
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
try
{
this.CoreService = coreService;
switch (args.Length)
{
case >= 1 when args[0] is IPEndPoint remoteHost:
this.ConnectorAdapter = new MegaTcpClient { Logger = coreService.Logger, Name = this.Name };
if (this.ConnectorAdapter != null)
{
this.ConnectorAdapter.DataReceived += this.ConnectorOnDataReceived;
}
this.ConnectionState = await this.ConnectorAdapter.ConnectAsync(remoteHost);
break;
}
return string.Empty;
}
catch (Exception ex)
{
this.Logger.LogException(ex);
return ex.Message;
}
}
private void ConnectorOnDataReceived(object sender, TcpMessage e)
{
var msg = e.ParseStirng();
if (string.IsNullOrEmpty(msg)) return;
this.Logger.LogInformation($"接收到扫码枪{this.Name}数据:{msg}");
this.OnDataReceived(this, e);
}
public override async Task<string> ReadAsnyc(TimeSpan timeout)
{
try
{
this.Stop();
await Task.Delay(20);
byte[] sendBytes = this.ConnectorAdapter.StringEncoder.GetBytes(this.StartCommandLine);
var tcpMessage = await this.ConnectorAdapter.WriteAndGetReplyAsync(sendBytes, timeout);
if (tcpMessage is null)
{
return null;
}
this.Code = this.ParseMessage(tcpMessage.ParseStirng(), tcpMessage.Data).TrimEnd();
this.Stop();
this.OnScannedEvent(this.Code);
return this.Code;
}
catch (Exception ex)
{
this.Logger.LogException(ex);
}
return null;
}
}
}

View File

@ -0,0 +1,42 @@
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MegaRobo.Connections;
namespace MegaRobo.C00225155.ControlDevices.Scanner
{
public class ScannerService: ScannerCodeOfTcpClientBase
{
public override bool IsConnected => this.ConnectionState == ConnectionState.IsConnected;
public override Task<string> ReadAsnyc(TimeSpan timeout)
{
return base.ReadAsnyc(timeout);
}
protected override string ParseMessage(object data, byte[] sourceData)
{
return data?.ToString() ?? string.Empty;
}
public override Task<ConnectionState> CheckConnected()
{
return base.CheckConnected();
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
}
}

View File

@ -0,0 +1,798 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
namespace MegaRobo.C00225155.ControlDevices
{
public enum SerialPortDataFormat
{
Char,
Hex
}
public class SerialPortService : IDisposable
{
private SerialPort serialPort;
private bool isCloseing = false;
private int tryReadNumber = 3;
public SerialConfigOptions SerialConfigOptions => new SerialConfigOptions
{
BaudRate = BaudRate,
DataBits = DataBits,
Handshake = Handshake,
Parity = Parity,
PortName = PortName,
StopBits = StopBits
};
public Stream BaseStream => serialPort.BaseStream;
public int BaudRate
{
get
{
return serialPort.BaudRate;
}
set
{
serialPort.BaudRate = value;
}
}
public bool BreakState
{
get
{
return serialPort.BreakState;
}
set
{
serialPort.BreakState = value;
}
}
public int BytesToRead => serialPort.BytesToRead;
public int BytesToWrite => serialPort.BytesToWrite;
public bool CDHolding => serialPort.CDHolding;
public bool CtsHolding => serialPort.CtsHolding;
public int DataBits
{
get
{
return serialPort.DataBits;
}
set
{
serialPort.DataBits = value;
}
}
public bool DiscardNull
{
get
{
return serialPort.DiscardNull;
}
set
{
serialPort.DiscardNull = value;
}
}
public bool DsrHolding => serialPort.DsrHolding;
public bool DtrEnable
{
get
{
return serialPort.DtrEnable;
}
set
{
serialPort.DtrEnable = value;
}
}
public Encoding Encoding
{
get
{
return serialPort.Encoding;
}
set
{
serialPort.Encoding = value;
}
}
public Handshake Handshake
{
get
{
return serialPort.Handshake;
}
set
{
serialPort.Handshake = value;
}
}
public bool IsOpen => serialPort?.IsOpen ?? false;
public string NewLine
{
get
{
return serialPort.NewLine;
}
set
{
serialPort.NewLine = value;
}
}
public Parity Parity
{
get
{
return serialPort.Parity;
}
set
{
serialPort.Parity = value;
}
}
public byte ParityReplace
{
get
{
return serialPort.ParityReplace;
}
set
{
serialPort.ParityReplace = value;
}
}
public string PortName
{
get
{
return serialPort.PortName;
}
set
{
serialPort.PortName = value;
}
}
public int ReadBufferSize
{
get
{
return serialPort.ReadBufferSize;
}
set
{
serialPort.ReadBufferSize = value;
}
}
public int ReadTimeout
{
get
{
return serialPort.ReadTimeout;
}
set
{
serialPort.ReadTimeout = value;
}
}
public int ReceivedBytesThreshold
{
get
{
return serialPort.ReceivedBytesThreshold;
}
set
{
serialPort.ReceivedBytesThreshold = value;
}
}
public bool RtsEnable
{
get
{
return serialPort.RtsEnable;
}
set
{
serialPort.RtsEnable = value;
}
}
public StopBits StopBits
{
get
{
return serialPort.StopBits;
}
set
{
serialPort.StopBits = value;
}
}
public int WriteBufferSize
{
get
{
return serialPort.WriteBufferSize;
}
set
{
serialPort.WriteBufferSize = value;
}
}
public int WriteTimeout
{
get
{
return serialPort.WriteTimeout;
}
set
{
serialPort.WriteTimeout = value;
}
}
public int TryReadSpanTime { get; set; } = 10;
public string Terminator { get; set; } = null;
public SerialPortDataFormat DataFormat { get; set; } = SerialPortDataFormat.Char;
public int TryReadNumber
{
get
{
return tryReadNumber;
}
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("TryReadNumber", "TryReadNumber must be equal or greater than 1.");
}
tryReadNumber = value;
}
}
public event EventHandler<Message> DataReceived;
public static string[] GetPortNames()
{
return SerialPort.GetPortNames();
}
public SerialPortService()
{
}
public SerialPortService(string portName)
: this(portName, 9600, 8, StopBits.One, Parity.None, Handshake.None)
{
}
public SerialPortService(string portName, int baudRate)
: this(portName, baudRate, 8, StopBits.One, Parity.None, Handshake.None)
{
}
public SerialPortService(string portName, int baudRate = 9600, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, Handshake handshake = Handshake.None)
{
if (portName == null)
{
throw new ArgumentNullException("portName");
}
if (dataBits < 7 || dataBits > 8)
{
throw new ArgumentNullException("dataBits", "only 7,8");
}
serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits)
{
Handshake = handshake,
WriteTimeout = 3000,
ReadTimeout = 4000
};
}
public SerialPortService(SerialConfigOptions options)
: this(options.PortName, options.BaudRate, options.DataBits, options.StopBits, options.Parity, options.Handshake)
{
if (options == null)
{
throw new ArgumentNullException("options");
}
}
public void Open()
{
if (serialPort == null)
{
throw new InvalidOperationException("Not initialized");
}
if (!serialPort.IsOpen)
{
serialPort.Open();
serialPort.DataReceived += SerialPort_DataReceived;
}
}
public void Close()
{
if (IsOpen)
{
isCloseing = true;
Thread.Sleep(TryReadSpanTime + 5);
serialPort.DataReceived -= SerialPort_DataReceived;
Thread.Sleep(TryReadSpanTime + 5);
serialPort.Close();
}
}
public void DiscardInBuffer()
{
serialPort.DiscardInBuffer();
}
public void DiscardOutBuffer()
{
serialPort.DiscardOutBuffer();
}
public void Dispose()
{
Close();
serialPort = null;
}
public int ReadByte()
{
return serialPort.ReadByte();
}
public int ReadChar()
{
return serialPort.ReadChar();
}
public string ReadLine()
{
return serialPort.ReadLine();
}
public string ReadExisting()
{
return serialPort.ReadExisting();
}
public string ReadTo(string value)
{
return serialPort.ReadTo(value);
}
public string ReadString()
{
try
{
string result = null;
byte[] array = TryRead();
if (array != null && array.Length != 0)
{
switch (DataFormat)
{
case SerialPortDataFormat.Char:
result = serialPort.Encoding.GetString(array);
break;
case SerialPortDataFormat.Hex:
result = array.ToHexString();
break;
}
}
return result;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
public byte[] Read()
{
try
{
return TryRead();
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
private byte[] TryRead()
{
int num = 0;
int num2 = 0;
bool flag = false;
List<byte> list = new List<byte>();
byte[] array = null;
if (Terminator != null)
{
array = Terminator.HexToByte();
num2 = array.Length;
}
while ((serialPort.BytesToRead > 0 || !flag) && num < tryReadNumber)
{
int num3 = ((serialPort.BytesToRead < serialPort.ReadBufferSize) ? serialPort.BytesToRead : serialPort.ReadBufferSize);
byte[] array2 = new byte[num3];
serialPort.Read(array2, 0, array2.Length);
list.AddRange(array2);
if (array != null && num2 > 0)
{
byte[] array3 = new byte[num2];
if (array2.Length >= num2)
{
Buffer.BlockCopy(array2, array2.Length - num2, array3, 0, num2);
}
else
{
if (list.ToArray().Length < num2)
{
continue;
}
byte[] array4 = list.ToArray();
Buffer.BlockCopy(array4, array4.Length - num2, array3, 0, num2);
}
flag = array.Length != 0 && array3.SequenceEqual(array);
}
if (TryReadSpanTime > 0)
{
Thread.Sleep(TryReadSpanTime);
}
num++;
}
return (list.Count > 0) ? list.ToArray() : null;
}
public int WriteHexString(string str)
{
try
{
byte[] array = str.HexToByte();
return Write(array, 0, array.Length);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
public int WriteAsciiString(string str)
{
try
{
byte[] bytes = serialPort.Encoding.GetBytes(str);
return Write(bytes, 0, bytes.Length);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
public int Write(byte[] bytes)
{
return Write(bytes, 0, bytes.Length);
}
public void Write(string text)
{
serialPort.Write(text);
}
public int Write(byte[] bytes, int offset, int count)
{
try
{
serialPort.DiscardInBuffer();
serialPort.Write(bytes, offset, count);
return count;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
}
public void WriteLine(string str)
{
serialPort.WriteLine(str);
}
public byte[] SendAndReceive(IEnumerable<byte> datas, TimeSpan timeout)
{
byte[] mReply = null;
DataReceived += SerialPortDataReceived;
int num = Write(datas.ToArray());
Stopwatch stopwatch = Stopwatch.StartNew();
while (mReply == null && (double)stopwatch.ElapsedMilliseconds < timeout.TotalMilliseconds)
{
Thread.Sleep(50);
}
DataReceived -= SerialPortDataReceived;
return mReply;
void SerialPortDataReceived(object sender, Message msg)
{
mReply = msg.Data;
}
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (!isCloseing)
{
byte[] data = TryRead();
Message e2 = new Message(data, Encoding);
this.DataReceived?.Invoke(this, e2);
}
}
}
/// <summary>
/// 扩展方法 byte[]
/// </summary>
public static class Extension
{
public static string ToHexString(this byte[] bytes, string separator = " ")
{
if (bytes == null || bytes.Length < 1)
{
return null;
}
return string.Join(separator, bytes.Select((byte b) => b.ToString("X2")).ToArray());
}
public static byte[] HexToByte(this string hex)
{
hex = hex.Replace(" ", "");
byte[] array = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2)
{
array[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return array;
}
}
public class SerialConfigOptions
{
public string PortName { get; set; }
public int BaudRate { get; set; } = 9600;
public int DataBits { get; set; } = 8;
public StopBits StopBits { get; set; } = StopBits.One;
public Parity Parity { get; set; } = Parity.None;
public Handshake Handshake { get; set; } = Handshake.None;
public SerialConfigOptions()
{
}
public SerialConfigOptions(string portName, int baudRate = 9600, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, Handshake handshake = Handshake.None)
{
PortName = portName ?? throw new ArgumentNullException("portName");
if (dataBits < 7 || dataBits > 8)
{
throw new ArgumentNullException("dataBits", "only 7,8");
}
BaudRate = baudRate;
DataBits = dataBits;
StopBits = stopBits;
Parity = parity;
Handshake = handshake;
}
public SerialConfigOptions(string portName, int baudRate = 9600, int dataBits = 8, int stopBits = 1, int parity = 0, int handshake = 0)
: this(portName, baudRate, dataBits, GetStopBits(stopBits), GetParity(parity), GetHandshake(handshake))
{
}
public SerialConfigOptions(string portName, int baudRate = 9600, int dataBits = 8, string stopBits = "one", string parity = "none", string handshake = "none")
: this(portName, baudRate, dataBits, GetStopBits(stopBits), GetParity(parity), GetHandshake(handshake))
{
}
public static Parity GetParity(string parityVal)
{
switch (parityVal?.ToLower())
{
case "0":
case "n":
case "none":
return Parity.None;
case "1":
case "o":
case "odd":
return Parity.Odd;
case "2":
case "e":
case "even":
return Parity.Even;
case "3":
case "m":
case "mark":
return Parity.Mark;
case "4":
case "s":
case "space":
return Parity.Space;
default:
return Parity.None;
}
}
public static Parity GetParity(int parityVal)
{
return GetParity(parityVal.ToString());
}
public static StopBits GetStopBits(string stopBitsVal)
{
switch (stopBitsVal?.ToLower())
{
case "0":
case "n":
case "none":
return StopBits.None;
case "1":
case "o":
case "one":
return StopBits.One;
case "2":
case "t":
case "two":
return StopBits.Two;
case "3":
case "1.5":
case "f":
case "of":
case "opf":
case "onepointfive":
return StopBits.OnePointFive;
default:
return StopBits.One;
}
}
public static StopBits GetStopBits(int stopBitsVal)
{
return GetStopBits(stopBitsVal.ToString());
}
public static Handshake GetHandshake(string shake)
{
switch (shake?.ToLower())
{
case "0":
case "n":
case "none":
return Handshake.None;
case "1":
case "x":
case "xoxo":
case "xonxoff":
case "software":
return Handshake.XOnXOff;
case "2":
case "r":
case "rts":
case "requesttosend":
case "hardware":
return Handshake.RequestToSend;
case "3":
case "rx":
case "rtsxx":
case "requesttosendxonxoff":
case "both":
return Handshake.RequestToSendXOnXOff;
default:
return Handshake.None;
}
}
public static Handshake GetHandshake(int shake)
{
return GetHandshake(shake.ToString());
}
}
public class Message
{
private readonly Encoding _encoder = null;
public byte[] Data { get; private set; }
public string MessageString => _encoder.GetString(Data);
public Message(byte[] data, Encoding stringEncoder)
{
Data = data;
_encoder = stringEncoder;
}
public string ReadString(SerialPortDataFormat dataFormat= SerialPortDataFormat.Char)
{
string result = null;
byte[] data = Data;
if (Data != null && Data.Length != 0)
{
switch (dataFormat)
{
case SerialPortDataFormat.Char:
result = _encoder.GetString(data);
break;
case SerialPortDataFormat.Hex:
result = data.ToHexString();
break;
}
}
return result;
}
}
}

View File

@ -0,0 +1,464 @@
using MegaRobo.Connections;
using MegaRobo.Connections.Adapters;
using MegaRobo.Connections.Modbus;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using static MegaRobo.Connections.Modbus.ModbusParams;
namespace MegaRobo.C00225155.ControlDevices
{
/// <summary>
/// 高温震荡电机驱动器控制服务
/// </summary>
public class HighTemperatureMotorControlService : ConnectorBase<MegaTcpClient>
{
/// <summary>
/// 寄存器地址枚举十六进制转十进制Modbus 需传入十进制地址)
/// </summary>
private enum MotorRegisterAddress : ushort
{
= 0x00, // 0x00十进制0启停控制
= 0x02, // 0x02十进制2模式切换
= 0x06, // 0x06十进制6速度指令
= 0x4A, // 0x4A十进制74清除故障
= 0xE3, // 0xE3十进制227驱动器状态
= 0xE4 // 0xE4十进制228电机实际转速
}
/// <summary>
/// 写入数据枚举
/// </summary>
private enum MotorWriteData : ushort
{
= 0x0000, // 启停控制0=停止
= 0x0001, // 启停控制1=启动
= 0x00C4, // 工作模式0x00C4=速度模式
= 0x00D0 // 工作模式0x00D0=位置模式
}
/// <summary>
/// 驱动器状态位解析(对应 0xE3 寄存器的 bit 位)
/// </summary>
public class MotorState
{
public bool { get; set; } // bit00=停机1=启动
public bool { get; set; } // bit11=故障
public bool { get; set; } // bit21=故障
public bool { get; set; } // bit31=故障
public bool { get; set; }// bit41=故障
public bool { get; set; } // bit51=故障
public bool { get; set; } // bit61=故障
public bool { get; set; }// bit70=PC控制1=PLC控制
public string { get; set; } // 汇总故障信息
}
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
CoreService = coreService;
try
{
base.CoreService = coreService;
if (args is [IPEndPoint remoteHost, ..])
{
base.ConnectorAdapter = new MegaTcpClient
{
Logger = coreService.Logger,
Name = base.Name
};
if (base.ConnectorAdapter != null)
{
base.ConnectorAdapter.DataReceived += ConnectorOnDataReceived;
}
ConnectionState = await base.ConnectorAdapter.ConnectAsync(remoteHost);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
return await Task.FromResult(ex.Message);
}
return string.Empty;
}
private void ConnectorOnDataReceived(object sender, TcpMessage tcpMsg)
{
if (tcpMsg is null || tcpMsg.Data == null || tcpMsg.Data.Length == 0)
return;
string hexData = BitConverter.ToString(tcpMsg.Data).Replace("-", " ");
Logger.LogInformation($"接收到电机驱动器数据(十六进制):{hexData}");
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
public override async Task<ConnectionState> CheckConnected()
{
var state = ConnectionState.None;
if (ConnectorAdapter is not null)
{
state = await ConnectorAdapter.CheckConnectedAsync();
}
ConnectionState = state;
return state;
}
/// <summary>
/// 电机启停控制
/// </summary>
/// <param name="id">设备从站号</param>
/// <param name="isStart"></param>
/// <returns></returns>
public async Task<bool> ControlMotorStartStop(byte id, bool isStart)
{
try
{
// 1. 确定写入数据:启动=0x0001停止=0x0000
MotorWriteData writeData = isStart ? MotorWriteData. : MotorWriteData.;
// 2. 调用 Modbus 写单个寄存器功能码06地址=启停控制0x00字节顺序AB高字节在前
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: id, // 从站地址默认1
startAdress: (ushort)MotorRegisterAddress.,
lstData: (ushort)writeData,
byteOrder: ByteOrder.AB // 协议要求高字节在前
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"启停控制指令构建失败:{writeResult.Message}");
return false;
}
// 3. 发送指令并获取响应
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(writeResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
Logger.LogError("启停控制未收到响应");
return false;
}
// 4. 验证响应(协议要求写成功后返回原报文)
bool isResponseMatch = CompareBytes(writeResult.Respond, tcpMessage.Data);
if (!isResponseMatch)
{
Logger.LogError($"启停控制响应不匹配:发送={BitConverter.ToString(writeResult.Respond)}, 接收={BitConverter.ToString(tcpMessage.Data)}");
return false;
}
Logger.LogInformation($"{(isStart ? "" : "")}电机成功");
return true;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "HighTemperatureMotorControlService", "ControlMotorStartStop", 150, false);
return false;
}
}
/// <summary>
/// 设置电机速度(速度模式下)
/// </summary>
/// <param name="id">设备从站号</param>
/// <param name="targetSpeed">目标速度0-3000 RPM协议上限3000</param>
public async Task<bool> SetMotorSpeed(byte id, int targetSpeed)
{
try
{
// 1. 参数校验:速度范围 0-3000 RPM协议规定
if (targetSpeed < 0 || targetSpeed > 3000)
{
Logger.LogError($"速度值超出范围0-3000 RPM{targetSpeed}");
return false;
}
// 2. 切换到速度模式(必须先设置模式,再写速度)
bool modeSuccess = await SwitchToSpeedMode(id);
if (!modeSuccess)
{
Logger.LogError("切换速度模式失败,无法设置速度");
return false;
}
// 3. 计算速度指令值:写入值 = (目标速度 / 3000) * 8192协议公式
ushort speedCommand = (ushort)Math.Round((targetSpeed / 3000.0) * 8192);
Logger.LogInformation($"目标速度={targetSpeed} RPM计算后指令值=0x{speedCommand:X4}");
// 4. 写速度指令到寄存器 0x06功能码06
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: id,
startAdress: (ushort)MotorRegisterAddress.,
lstData: speedCommand,
byteOrder: ByteOrder.AB // 高字节在前
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"速度指令构建失败:{writeResult.Message}");
return false;
}
// 5. 发送指令并验证响应
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(writeResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
Logger.LogError("设置速度未收到响应");
return false;
}
// 6. 验证响应(写成功返回原报文)
bool isResponseMatch = CompareBytes(writeResult.Respond, tcpMessage.Data);
if (!isResponseMatch)
{
Logger.LogError($"速度设置响应不匹配:发送={BitConverter.ToString(writeResult.Respond)}, 接收={BitConverter.ToString(tcpMessage.Data)}");
return false;
}
Logger.LogInformation($"设置电机速度成功:{targetSpeed} RPM指令值=0x{speedCommand:X4}");
return true;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "HighTemperatureMotorControlService", "SetMotorSpeed", 150, false);
return false;
}
}
/// <summary>
/// 获取电机驱动器状态(运行状态+故障信息)
/// </summary>
/// <param name="id">设备从站号</param>
public async Task<MotorState> GetMotorState(byte id)
{
try
{
// 1. 构建读状态指令地址0xE3十进制227读1个寄存器状态是16位
var readResult = ModbusRTU.GetReadMessage(
slaveStation: id,
readType: ModbusReadCodes.READ_HOLDING_REGISTERS, // 功能码03
startAdr: (ushort)MotorRegisterAddress.,
length: 1 // 状态寄存器是1个16位寄存器长度=1
);
if (!readResult.IsSuccessed)
{
Logger.LogError($"读状态指令构建失败:{readResult.Message}");
return new MotorState { = "指令构建失败" };
}
// 2. 发送指令并获取响应
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(readResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
Logger.LogError("读状态未收到响应");
return new MotorState { = "未收到响应" };
}
// 3. 解析响应16位状态字字节顺序AB
var parseResult = ModbusRTU.ReadRegisterUShort(tcpMessage.Data, ByteOrder.AB);
if (!parseResult.IsSuccessed)
{
Logger.LogError($"状态解析失败:{parseResult.Message}");
return new MotorState { = parseResult.Message };
}
// 4. 解析状态字的各个bit位协议表5-22
ushort statusWord = parseResult.Respond[0];
var motorState = new MotorState
{
= (statusWord & (1 << 0)) != 0, // bit0
= (statusWord & (1 << 1)) != 0, // bit1
= (statusWord & (1 << 2)) != 0, // bit2
= (statusWord & (1 << 3)) != 0, // bit3
= (statusWord & (1 << 4)) != 0,// bit4
= (statusWord & (1 << 5)) != 0, // bit5
= (statusWord & (1 << 6)) != 0, // bit6
= (statusWord & (1 << 7)) != 0 // bit7
};
// 5. 汇总故障描述
var errors = new List<string>();
if (motorState.) errors.Add("过流");
if (motorState.) errors.Add("过压");
if (motorState.) errors.Add("编码器故障");
if (motorState.) errors.Add("位置偏差过大");
if (motorState.) errors.Add("欠压");
if (motorState.) errors.Add("过载");
motorState. = errors.Count > 0
? $"故障:{string.Join("", errors)}"
: "无故障";
Logger.LogInformation($"电机状态:运行状态={(motorState.运行状态 ? "" : "")}{motorState.故障描述}");
return motorState;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "HighTemperatureMotorControlService", "GetMotorState", 150, false);
return new MotorState { = $"异常:{ex.Message}" };
}
}
/// <summary>
/// 获取电机实际转速
/// </summary>
/// <param name="id">设备从站号</param>
public async Task<int> GetMotorActualSpeed(byte id)
{
try
{
// 1. 构建读转速指令地址0xE4十进制228读1个寄存器
var readResult = ModbusRTU.GetReadMessage(
slaveStation: id,
readType: ModbusReadCodes.READ_HOLDING_REGISTERS,
startAdr: (ushort)MotorRegisterAddress.,
length: 1
);
if (!readResult.IsSuccessed)
{
Logger.LogError($"读转速指令构建失败:{readResult.Message}");
return -1;
}
// 2. 发送指令并获取响应
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(readResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
Logger.LogError("读转速未收到响应");
return -1;
}
// 3. 解析转速16位无符号数直接表示RPM
var parseResult = ModbusRTU.ReadRegisterUShort(tcpMessage.Data, ByteOrder.AB);
if (!parseResult.IsSuccessed)
{
Logger.LogError($"转速解析失败:{parseResult.Message}");
return -1;
}
int actualSpeed = parseResult.Respond[0];
Logger.LogInformation($"电机实际转速:{actualSpeed} RPM");
return actualSpeed;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "HighTemperatureMotorControlService", "GetMotorActualSpeed", 150, false);
return -1;
}
}
/// <summary>
/// 清除电机故障
/// </summary>
/// <param name="id">设备从站号</param>
/// <returns></returns>
public async Task<bool> ClearMotorFault(byte id)
{
try
{
// 协议规定写入任意值到0x4A即可清除故障此处写0x0000
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: id,
startAdress: (ushort)MotorRegisterAddress.,
lstData: (ushort)0x0000,
byteOrder: ByteOrder.AB
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"清除故障指令构建失败:{writeResult.Message}");
return false;
}
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(writeResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
Logger.LogError("清除故障未收到响应");
return false;
}
bool isResponseMatch = CompareBytes(writeResult.Respond, tcpMessage.Data);
if (isResponseMatch)
{
Logger.LogInformation("清除电机故障成功");
return true;
}
else
{
Logger.LogError("清除故障响应不匹配");
return false;
}
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "HighTemperatureMotorControlService", "ClearMotorFault", 150, false);
return false;
}
}
/// <summary>
/// 切换到速度模式内部调用设置模式寄存器0x02为0x00C4
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private async Task<bool> SwitchToSpeedMode(byte id)
{
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: id,
startAdress: (ushort)MotorRegisterAddress.,
lstData: (ushort)MotorWriteData.,
byteOrder: ByteOrder.AB
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"切换速度模式指令构建失败:{writeResult.Message}");
return false;
}
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(writeResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
Logger.LogError("切换速度模式未收到响应");
return false;
}
bool isMatch = CompareBytes(writeResult.Respond, tcpMessage.Data);
if (isMatch)
{
Logger.LogInformation("切换到速度模式成功");
return true;
}
else
{
Logger.LogError("切换速度模式响应不匹配");
return false;
}
}
/// <summary>
/// 比较两个字节数组是否相等(验证响应报文)
/// </summary>
private bool CompareBytes(byte[] source, byte[] target)
{
if (source.Length != target.Length) return false;
for (int i = 0; i < source.Length; i++)
{
if (source[i] != target[i]) return false;
}
return true;
}
}
}

View File

@ -0,0 +1,110 @@
using MegaRobo.Connections;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class HighTemperatureShakeService : ConnectorBase<MegaTcpClient>
{
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
this.CoreService = coreService;
try
{
base.CoreService = coreService;
if (args is [IPEndPoint remoteHost, ..])
{
base.ConnectorAdapter = new MegaTcpClient
{
Logger = coreService.Logger,
Name = base.Name
};
if (base.ConnectorAdapter != null)
{
base.ConnectorAdapter.DataReceived += ConnectorOnDataReceived;
}
ConnectionState = await base.ConnectorAdapter.ConnectAsync(remoteHost);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
return await Task.FromResult(ex.Message);
}
return string.Empty;
}
private void ConnectorOnDataReceived(object sender, TcpMessage tcpMsg)
{
if (tcpMsg is null)
return;
var msg = tcpMsg.ParseStirng();
if (string.IsNullOrEmpty(msg)) return;
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
/// <summary>
/// 检测连接状态
/// </summary>
/// <returns></returns>
public override async Task<ConnectionState> CheckConnected()
{
var state = ConnectionState.None;
if (ConnectorAdapter is not null)
{
state = await ConnectorAdapter.CheckConnectedAsync();
}
ConnectionState = state;
return state;
}
/// <summary>
/// 设置加热温度
/// </summary>
/// <param name="temperature"></param>
/// <param name="deviceId"></param>
/// <param name="offTemp">关闭温度</param>
/// <returns></returns>
public async Task<bool> SetTemperature(int temperature, byte deviceId, bool offTemp)
{
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(HighTemperatureUartHCM.SendSetHotTemp(temperature, deviceId, offTemp), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return false;
ParseResult result = HighTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
return result.IsSuccess;
}
/// <summary>
/// 获取当前温度
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<int> GetActualTemperature(byte deviceId)
{
int temperature = 0;
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(HighTemperatureUartHCM.SendReadTempRequest(deviceId), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return 0;
ParseResult result = HighTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
if (result.IsSuccess)
{
temperature = (int)result.Data["实际温度(℃)"];
}
return temperature;
}
}
}

View File

@ -0,0 +1,442 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class HighTemperatureUartHCM
{
#region
private const byte ST = 0x5E; // 帧头
private const byte HELLO = 0x01; // 握手指令
private const byte RDTEMP = 0x08; // 读取温度
private const byte RDADC = 0x09; // 读取传感器AD
private const byte RDEE = 0x11; // 读取EEPROM
private const byte WREE = 0x12; // 写入EEPROM
private const byte Calb = 0x15; // 校准温度
private const byte SETHOTTEMP = 0x16; // 加热温度控制
private const byte RESPONSE_FLAG = 0x80; // 上传指令标志
#endregion
#region 6n-18
/// <summary>
/// 计算帧的校验和核心前length字节之和的低8位
/// </summary>
private static byte CalculateChecksum(byte[] data, int length)
{
if (data == null || length <= 0 || length > data.Length)
throw new ArgumentException("校验和计算参数无效");
int sum = 0;
for (int i = 0; i < length; i++)
sum += data[i];
return (byte)(sum & 0xFF); // 取低8位确保1字节
}
#endregion
#region 1.1g>2msThread.Sleep确保阻塞
/// <summary>
/// 帧间隔时间预留3ms余量确保符合协议要求
/// </summary>
private static void AddFrameInterval()
{
Task.Delay(3);
}
#endregion
#region PC
/// <summary>
/// 握手指令(系统开机必发)
/// </summary>
public static byte[] SendHello(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 协议固定帧长5字节
// 帧结构ST → HELLO(0x01) → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = HELLO; // 移除序号或运算CMD为固定指令码协议3.1
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4); // 前4字节求和
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取温度指令
/// </summary>
public static byte[] SendReadTempRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 协议固定帧长5字节
// 帧结构ST → RDTEMP(0x08) → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = RDTEMP;
frame[2] = deviceId;
frame[3] = 5;
frame[4] = CalculateChecksum(frame, 4);
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取传感器AD指令
/// </summary>
public static byte[] SendReadAdcRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 协议固定帧长5字节
// 帧结构ST → RDADC(0x09) → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = RDADC;
frame[2] = deviceId;
frame[3] = 5;
frame[4] = CalculateChecksum(frame, 4);
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取EEPROM指令
/// </summary>
public static byte[] SendReadEepromRequest(byte addrHigh, byte addrMid, byte addrLow, int readLength, byte deviceId = 0)
{
if (readLength < 1)
throw new ArgumentOutOfRangeException(nameof(readLength), "读取长度必须≥1");
int frameLength = 9 + readLength; // 帧长=9固定段+ 读取长度协议3.4
byte[] frame = new byte[frameLength];
// 帧结构ST → RDEE(0x11) → 设备ID → LTH=N → 地址高→中→低 → 读取长度 → 校验和
frame[0] = ST;
frame[1] = RDEE;
frame[2] = deviceId;
frame[3] = (byte)frameLength;
frame[4] = addrHigh;
frame[5] = addrMid;
frame[6] = addrLow;
frame[7] = (byte)readLength;
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
AddFrameInterval();
return frame;
}
/// <summary>
/// 写入EEPROM指令
/// </summary>
public static byte[] SendWriteEepromRequest(byte addrHigh, byte addrMid, byte addrLow, byte[] writeData, byte deviceId = 0)
{
if (writeData == null || writeData.Length == 0)
throw new ArgumentException("写入数据不能为空", nameof(writeData));
int frameLength = 9 + writeData.Length; // 帧长=9固定段+ 数据长度协议3.5
byte[] frame = new byte[frameLength];
// 帧结构ST → WREE(0x12) → 设备ID → LTH=N → 地址高→中→低 → 写入数据 → 校验和
frame[0] = ST;
frame[1] = WREE;
frame[2] = deviceId;
frame[3] = (byte)frameLength;
frame[4] = addrHigh;
frame[5] = addrMid;
frame[6] = addrLow;
Array.Copy(writeData, 0, frame, 7, writeData.Length);
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
AddFrameInterval();
return frame;
}
/// <summary>
/// 校准温度指令设置B值=(实际温度-目标温度)×100
/// </summary>
public static byte[] SendCalibrateTempRequest(int calibBValue, byte deviceId = 0)
{
if (calibBValue < short.MinValue || calibBValue > short.MaxValue)
throw new ArgumentOutOfRangeException(nameof(calibBValue), "校准B值必须在-32768~32767之间");
byte[] frame = new byte[7]; // 协议固定帧长7字节
short bParam = (short)calibBValue; // 16位有符号数支持负值
// 帧结构ST → Calb(0x15) → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = Calb;
frame[2] = deviceId;
frame[3] = 7;
frame[4] = (byte)(bParam >> 8); // 高位在前协议3.6
frame[5] = (byte)(bParam & 0xFF);
frame[6] = CalculateChecksum(frame, 6);
AddFrameInterval();
return frame;
}
/// <summary>
/// 加热温度控制(设置目标温度或释放控制)
/// </summary>
public static byte[] SendSetHotTemp(double targetTemp, byte deviceId = 0, bool releaseControl = false)
{
byte[] frame = new byte[7]; // 协议固定帧长7字节
ushort tempParam;
if (releaseControl)
{
tempParam = 0xFFFF; // 协议约定:释放控制=0xFFFF关断功率
}
else
{
// 协议范围:室温~100℃对应室温×100~10000室温暂以20℃为参考
if (targetTemp < 20.0 || targetTemp > 100.0)
throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[20.0, 100.0]℃之间(符合协议室温要求)");
tempParam = (ushort)(targetTemp * 100); // 放大100倍协议3.7
}
// 帧结构ST → SETHOTTEMP(0x16) → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = SETHOTTEMP;
frame[2] = deviceId;
frame[3] = 7;
frame[4] = (byte)(tempParam >> 8); // 高位在前协议3.7
frame[5] = (byte)(tempParam & 0xFF);
frame[6] = CalculateChecksum(frame, 6);
AddFrameInterval();
return frame;
}
#endregion
#region
/// <summary>
/// 解析主控板返回的帧数据(入口方法)
/// </summary>
public static ParseResult ParseReceivedFrame(byte[] receivedFrame)
{
// 基础校验帧非空、最短帧≥6字节上传指令最短为握手响应6字节
if (receivedFrame == null || receivedFrame.Length < 6)
return new ParseResult(false, "无效帧:数据为空或长度不足", null);
// 1. 校验帧头
if (receivedFrame[0] != ST)
return new ParseResult(false, $"帧头错误实际0x{receivedFrame[0]:X2}期望0x{ST:X2}", null);
// 2. 提取核心字段
byte cmdWithFlag = receivedFrame[1];
byte deviceId = receivedFrame[2];
byte frameLength = receivedFrame[3];
byte checksum = receivedFrame[frameLength - 1];
// 3. 校验帧长(实际长度=帧长字段)
if (receivedFrame.Length != frameLength)
return new ParseResult(false, $"帧长不匹配:实际{receivedFrame.Length}字节,期望{frameLength}字节", null);
// 4. 校验和验证
byte calculatedChecksum = CalculateChecksum(receivedFrame, frameLength - 1);
if (calculatedChecksum != checksum)
return new ParseResult(false, $"校验和错误实际0x{checksum:X2}计算0x{calculatedChecksum:X2}", null);
// 5. 去除响应标志,获取原始指令码
byte originalCmd = (byte)(cmdWithFlag & ~RESPONSE_FLAG);
// 6. 按指令类型分发解析
switch (originalCmd)
{
case HELLO:
return ParseHelloResponse(receivedFrame, deviceId);
case RDTEMP:
return ParseReadTempResponse(receivedFrame, deviceId);
case RDADC:
return ParseReadAdcResponse(receivedFrame, deviceId);
case RDEE:
return ParseReadEepromResponse(receivedFrame, deviceId);
case WREE:
return ParseWriteEepromResponse(receivedFrame, deviceId);
case Calb:
return ParseCalibrateTempResponse(receivedFrame, deviceId);
case SETHOTTEMP:
return ParseSetHotTempResponse(receivedFrame, deviceId);
default:
return new ParseResult(false, $"未知指令0x{originalCmd:X2}", null);
}
}
#region
/// <summary>
/// 解析握手响应
/// </summary>
private static ParseResult ParseHelloResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "握手响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.1
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "握手成功" : $"握手失败(错误码:{status}" }
};
return new ParseResult(true, "握手响应解析成功", data);
}
/// <summary>
/// 解析读取温度响应
/// </summary>
private static ParseResult ParseReadTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 0x0D) // 协议固定响应帧长13字节0x0D
return new ParseResult(false, $"读取温度响应帧长错误需13字节实际{frame.Length}字节", null);
// 提取数据参数1(设置温度)、参数2(实际温度)、参数3(预留)、标志位、状态位协议3.2
ushort setTempParam = (ushort)((frame[4] << 8) | frame[5]); // 高位在前
ushort actualTempParam = (ushort)((frame[6] << 8) | frame[7]); // 高位在前
byte flag = frame[10]; // 预留标志位NC
byte status = frame[11];
// 转换为实际温度除以100精度0.1℃)
double setTemp = setTempParam / 100.0;
double actualTemp = actualTempParam / 100.0;
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取温度成功" : $"读取失败(错误码:{status}" },
{ "设置温度(℃)", setTemp },
{ "实际温度(℃)", actualTemp },
{ "预留标志位", flag },
{ "原始设置温度参数", $"0x{setTempParam:X4}" },
{ "原始实际温度参数", $"0x{actualTempParam:X4}" }
};
return new ParseResult(true, "读取温度响应解析成功", data);
}
/// <summary>
/// 解析读取AD响应
/// </summary>
private static ParseResult ParseReadAdcResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 0x0A) // 协议固定响应帧长10字节0x0A修正原13字节错误
return new ParseResult(false, $"读取AD响应帧长错误需10字节实际{frame.Length}字节", null);
// 提取数据AD值5-6字节、预留7-8字节、状态位9字节协议3.3
ushort adcValue = (ushort)((frame[4] << 8) | frame[5]); // 高位在前
byte status = frame[8];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取AD成功" : $"读取失败(错误码:{status}" },
{ "AD值", adcValue },
{ "原始AD参数", $"0x{adcValue:X4}" }
};
return new ParseResult(true, "读取AD响应解析成功", data);
}
/// <summary>
/// 解析读取EEPROM响应
/// </summary>
private static ParseResult ParseReadEepromResponse(byte[] frame, byte deviceId)
{
if (frame.Length < 6) // 协议响应帧长≥6字节
return new ParseResult(false, "读取EEPROM响应帧长不足需≥6字节", null);
// 提取数据参数1~n5到N-2字节、状态位N-2字节协议3.4
int dataLength = frame.Length - 6; // 数据字节数=N-6
byte[] eepromData = new byte[dataLength];
Array.Copy(frame, 4, eepromData, 0, dataLength);
byte status = frame[frame.Length - 2];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取EEPROM成功" : $"读取失败(错误码:{status}" },
{ "读取数据长度(字节)", dataLength },
{ "读取数据(十六进制)", BitConverter.ToString(eepromData).Replace("-", " ") },
{ "原始数据数组", eepromData }
};
return new ParseResult(true, "读取EEPROM响应解析成功", data);
}
/// <summary>
/// 解析写入EEPROM响应
/// </summary>
private static ParseResult ParseWriteEepromResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "写入EEPROM响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.5
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "写入EEPROM成功" : $"写入失败(错误码:{status}" }
};
return new ParseResult(true, "写入EEPROM响应解析成功", data);
}
/// <summary>
/// 解析校准温度响应
/// </summary>
private static ParseResult ParseCalibrateTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "校准温度响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.6
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "温度校准成功" : $"校准失败(错误码:{status}" }
};
return new ParseResult(true, "校准温度响应解析成功", data);
}
/// <summary>
/// 解析加热温度控制响应
/// </summary>
private static ParseResult ParseSetHotTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6) // 协议固定响应帧长6字节
return new ParseResult(false, "加热温度响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位协议3.7
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "加热温度指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "加热温度响应解析成功", data);
}
#endregion
#endregion
#region CRC16校验使
public static byte[] CRC16(byte[] data)
{
int len = data.Length;
if (len <= 0)
return new byte[] { 0, 0 };
ushort crc = 0xFFFF;
for (int i = 0; i < len; i++)
{
crc = (ushort)(crc ^ data[i]);
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8);
byte lo = (byte)(crc & 0x00FF);
return BitConverter.IsLittleEndian ? new byte[] { lo, hi } : new byte[] { hi, lo };
}
#endregion
}
}

View File

@ -0,0 +1,179 @@
using MegaRobo.Connections;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using Npgsql.Replication.PgOutput.Messages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
public class LowTemperatureMagneticStirService : ConnectorBase<MegaTcpClient>
{
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
this.CoreService = coreService;
try
{
base.CoreService = coreService;
if (args is [IPEndPoint remoteHost, ..])
{
base.ConnectorAdapter = new MegaTcpClient
{
Logger = coreService.Logger,
Name = base.Name
};
if (base.ConnectorAdapter != null)
{
base.ConnectorAdapter.DataReceived += ConnectorOnDataReceived;
}
ConnectionState = await base.ConnectorAdapter.ConnectAsync(remoteHost);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
return await Task.FromResult(ex.Message);
}
return string.Empty;
}
private void ConnectorOnDataReceived(object sender, TcpMessage tcpMsg)
{
if (tcpMsg is null)
return;
var msg = tcpMsg.ParseStirng();
if (string.IsNullOrEmpty(msg)) return;
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
/// <summary>
/// 检测连接状态
/// </summary>
/// <returns></returns>
public override async Task<ConnectionState> CheckConnected()
{
var state = ConnectionState.None;
if (ConnectorAdapter is not null)
{
state = await ConnectorAdapter.CheckConnectedAsync();
}
ConnectionState = state;
return state;
}
/// <summary>
/// 设置电机转速
/// </summary>
/// <param name="motorSpeed"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<bool> SetMotorSpeed(int motorSpeed, byte deviceId)
{
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(LowTemperatureUartHCM.SendMotorControl(motorSpeed, deviceId), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return false;
ParseResult result = LowTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
return result.IsSuccess;
}
/// <summary>
/// 设置温度
/// </summary>
/// <param name="temperature"></param>
/// <param name="deviceId"></param>
/// <param name="offTemp">关闭温度</param>
/// <returns></returns>
public async Task<bool> SetTemperature(int temperature, byte deviceId, bool offTemp)
{
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(LowTemperatureUartHCM.SendSetTemp(temperature, deviceId, offTemp), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return false;
ParseResult result = LowTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
return result.IsSuccess;
}
/// <summary>
/// 设置加热温度,除霜
/// </summary>
/// <param name="hottemperature"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<bool> SetHotTemperature(int hottemperature, byte deviceId)
{
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(LowTemperatureUartHCM.SendSetHotTemp(hottemperature, deviceId), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return false;
ParseResult result = LowTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
return result.IsSuccess;
}
/// <summary>
/// 获取电机转速
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<int> GetActualMotorSpeed(byte deviceId)
{
int motorSpeed = 0;
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(LowTemperatureUartHCM.SendReadMotorSpeedRequest(deviceId), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return 0;
ParseResult result = LowTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
if (result.IsSuccess)
{
motorSpeed = (int)result.Data["实际转速"];
}
return motorSpeed;
}
/// <summary>
/// 获取当前温度
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<int> GetActualTemperature(byte deviceId)
{
int temperature = 0;
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(LowTemperatureUartHCM.SendReadTempRequest(deviceId), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return 0;
ParseResult result = LowTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
if (result.IsSuccess)
{
temperature = (int)result.Data["实际温度(℃)"];
}
return temperature;
}
/// <summary>
/// 获取开关盖状态
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<string> GetLidStatus(byte deviceId)
{
string status = string.Empty;
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(LowTemperatureUartHCM.SendReadIoStatusRequest(deviceId), TimeSpan.FromSeconds(3));
if (tcpMessage is null)
return string.Empty;
ParseResult result = LowTemperatureUartHCM.ParseReceivedFrame(tcpMessage.Data);
if (result.IsSuccess)
{
status = result.Data["开关盖状态"].ToString() ?? string.Empty;
}
return status;
}
}
}

View File

@ -0,0 +1,852 @@
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
/// <summary>
/// 高温温度模块 485 uart 定制HCM通信协议封装类PC→主控板指令构建 + 主控板→PC指令解析
/// </summary>
public static class LowTemperatureUartHCM
{
#region
private const byte ST = 0x5E; // 帧头
private const byte HELLO = 0x01; // 握手指令
private const byte STATUS = 0x05; // 读取报错状态
private const byte SETTEMP = 0x06; // 制冷温度控制
private const byte MOTOR = 0x07; // 电机控制
private const byte RDTEMP = 0x08; // 读取温度
private const byte RDADC = 0x09; // 读取传感器AD
private const byte RDEE = 0x11; // 读取EEPROM
private const byte WREE = 0x12; // 写入EEPROM
private const byte RDMotSpeed = 0x13; // 获取实际转速
private const byte RDIOStatus = 0x14; // 获取开关盖状态
private const byte Calb = 0x15; // 校准温度
private const byte SETHOTTEMP = 0x16; // 加热温度控制(补充遗漏指令)
private const byte RelayControl = 0x17; // 电磁阀控制(补充遗漏指令)
private const byte RESPONSE_FLAG = 0x80; // 上传指令标志(指令码 | 0x80
#endregion
#region
private static byte _cmdSequence = 0; // 指令序号0-255自动溢出
private static readonly object _lock = new object();// 线程安全锁(避免多线程序号混乱)
#endregion
#region 6n-18
/// <summary>
/// 计算帧的校验和核心前length字节之和的低8位
/// </summary>
/// <param name="data">待计算的帧数据</param>
/// <param name="length">参与计算的字节数前n-1字节</param>
/// <returns>校验和字节</returns>
private static byte CalculateChecksum(byte[] data, int length)
{
if (data == null || length <= 0 || length > data.Length)
throw new ArgumentException("校验和计算参数无效");
int sum = 0;
for (int i = 0; i < length; i++)
sum += data[i];
return (byte)(sum & 0xFF); // 取低8位确保是1字节
}
#endregion
#region 线
/// <summary>
/// 指令序号自增0-255自动溢出确保除溢出外无重复指令
/// </summary>
private static void IncrementCmdSequence()
{
lock (_lock)
{
_cmdSequence++; // 字节类型自动溢出255→0
// 确保序号不超过0x7F避免下发指令CMD被误认为上传指令
if (_cmdSequence >= 0x80)
_cmdSequence = 0;
}
}
/// <summary>
/// 获取当前指令序号(带线程安全)
/// </summary>
private static byte GetCurrentCmdSequence()
{
lock (_lock)
{
return _cmdSequence;
}
}
#endregion
#region PCbyte[]
/// <summary>
/// 握手指令(系统开机必发)
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>握手指令帧5字节</returns>
public static byte[] SendHello(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 按协议填充帧结构ST → HELLO+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(HELLO | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4); // 前4字节求和取低8位
IncrementCmdSequence(); // 序号自增
AddFrameInterval(); // 帧间隔>2ms
return frame;
}
/// <summary>
/// 读取报错状态指令
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>报错状态指令帧5字节</returns>
public static byte[] SendStatusRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → STATUS+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(STATUS | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 温度控制指令(下发后主控板直接执行温度控制)
/// </summary>
/// <param name="targetTemp">目标温度10.0-180.0℃精度0.1</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <param name="releaseControl">是否释放控制true=0xFFFF关断功率关闭温度</param>
/// <returns>温度控制指令帧7字节</returns>
public static byte[] SendSetTemp(double targetTemp, byte deviceId = 0, bool releaseControl=false)
{
byte[] frame = new byte[7]; // 帧长固定7字节
byte seq = GetCurrentCmdSequence();
int tempParam = 0; // 温度参数16位高8位+低8位
// 处理参数:释放控制/正常温度
if (releaseControl)
{
tempParam = 0xFFFF; // 协议约定:释放控制=0xFFFF
}
else
{
// 参数校验温度范围10.0-180.0℃对应1000-18000
if (targetTemp < -50.0 || targetTemp > 50.0)
throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[-50 , 50]℃之间");
tempParam = (short)(targetTemp * 100); // 放大100倍精度0.1
}
// 帧结构ST → SETTEMP+序号 → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = (byte)(SETTEMP | seq);
frame[2] = deviceId;
frame[3] = 7; // LTH固定为7
frame[4] = (byte)(tempParam >> 8); // 参数1高8位
frame[5] = (byte)(tempParam & 0xFF); // 参数2低8位
frame[6] = CalculateChecksum(frame, 6); // 前6字节求和
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 电机控制指令(设置电机转速)
/// </summary>
/// <param name="motorSpeed">电机转速200-150016位参数</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>电机控制指令帧11字节0x0B=11</returns>
public static byte[] SendMotorControl(int motorSpeed, byte deviceId = 0)
{
// 参数校验转速范围100-1500
if (motorSpeed < 100 || motorSpeed > 1500)
throw new ArgumentOutOfRangeException(nameof(motorSpeed), "转速必须在100-1500之间");
byte[] frame = new byte[11]; // 帧长固定0x0B11字节
byte seq = GetCurrentCmdSequence();
ushort speedParam = (ushort)motorSpeed; // 转速参数16位
// 帧结构ST → MOTOR+序号 → 设备ID → LTH=0x0B → 0x10 → 预留0×3 → 参数4(高8) → 参数5(低8) → 校验和
frame[0] = ST;
frame[1] = (byte)(MOTOR | seq);
frame[2] = deviceId;
frame[3] = 0x0B; // LTH固定为11
frame[4] = 0x10; // 协议固定值
frame[5] = 0; // 预留位1
frame[6] = 0; // 预留位2
frame[7] = 0; // 预留位3
frame[8] = (byte)(speedParam >> 8); // 参数4高8位
frame[9] = (byte)(speedParam & 0xFF); // 参数5低8位
frame[10] = CalculateChecksum(frame, 10); // 前10字节求和
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取温度指令(获取设置温度、实际温度、到温标志)
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>读取温度指令帧5字节</returns>
public static byte[] SendReadTempRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDTEMP+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDTEMP | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取传感器AD指令获取AD值
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>读取AD指令帧5字节</returns>
public static byte[] SendReadAdcRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节协议3.6
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDADC+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDADC | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 读取EEPROM指令获取指定地址的硬件/调试信息)
/// </summary>
/// <param name="addrHigh">地址高位字节</param>
/// <param name="addrMid">地址中位字节</param>
/// <param name="addrLow">地址低位字节</param>
/// <param name="readLength">读取长度(字节数)</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>读取EEPROM指令帧变长9 + readLength字节</returns>
public static byte[] SendReadEepromRequest(byte addrHigh, byte addrMid, byte addrLow, int readLength, byte deviceId = 0)
{
// 参数校验读取长度≥1避免空数据
if (readLength < 1)
throw new ArgumentOutOfRangeException(nameof(readLength), "读取长度必须≥1");
int frameLength = 9 + readLength; // 帧长N=9固定段+ 读取长度
byte[] frame = new byte[frameLength];
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDEE+序号 → 设备ID → LTH=N → 地址高→中→低 → 读取长度 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDEE | seq);
frame[2] = deviceId;
frame[3] = (byte)frameLength; // LTH=N实际帧长
frame[4] = addrHigh; // 地址高位
frame[5] = addrMid; // 地址中位
frame[6] = addrLow; // 地址低位
frame[7] = (byte)readLength; // 读取长度
// 校验和frameLength-1字节求和帧长-1 = 8 + readLength
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 写入EEPROM指令写入硬件/调试/标定参数)
/// </summary>
/// <param name="addrHigh">地址高位字节</param>
/// <param name="addrMid">地址中位字节</param>
/// <param name="addrLow">地址低位字节</param>
/// <param name="writeData">待写入的数据(字节数组)</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>写入EEPROM指令帧变长9 + writeData.Length字节</returns>
public static byte[] SendWriteEepromRequest(byte addrHigh, byte addrMid, byte addrLow, byte[] writeData, byte deviceId = 0)
{
// 参数校验:写入数据非空
if (writeData == null || writeData.Length == 0)
throw new ArgumentException("写入数据不能为空", nameof(writeData));
int frameLength = 9 + writeData.Length; // 帧长N=9固定段+ 数据长度
byte[] frame = new byte[frameLength];
byte seq = GetCurrentCmdSequence();
// 帧结构ST → WREE+序号 → 设备ID → LTH=N → 地址高→中→低 → 写入数据 → 校验和
frame[0] = ST;
frame[1] = (byte)(WREE | seq);
frame[2] = deviceId;
frame[3] = (byte)frameLength; // LTH=N实际帧长
frame[4] = addrHigh; // 地址高位
frame[5] = addrMid; // 地址中位
frame[6] = addrLow; // 地址低位
// 填充写入数据从第8字节开始索引7
Array.Copy(writeData, 0, frame, 7, writeData.Length);
// 校验和frameLength-1字节求和
frame[frameLength - 1] = CalculateChecksum(frame, frameLength - 1);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 获取实际转速指令
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>获取转速指令帧5字节</returns>
public static byte[] SendReadMotorSpeedRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDMotSpeed+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDMotSpeed | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 获取开关盖状态指令
/// </summary>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>获取开关盖状态指令帧5字节</returns>
public static byte[] SendReadIoStatusRequest(byte deviceId = 0)
{
byte[] frame = new byte[5]; // 帧长固定5字节
byte seq = GetCurrentCmdSequence();
// 帧结构ST → RDIOStatus+序号 → 设备ID → LTH=5 → 校验和
frame[0] = ST;
frame[1] = (byte)(RDIOStatus | seq);
frame[2] = deviceId;
frame[3] = 5; // LTH固定为5
frame[4] = CalculateChecksum(frame, 4);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 校准温度指令设置温度校准B值
/// </summary>
/// <param name="calibBValue">校准B值16位B=(实际温度-目标温度)×100</param>
/// <param name="deviceId">设备ID0-255默认0</param>
/// <returns>校准温度指令帧7字节</returns>
public static byte[] SendCalibrateTempRequest(int calibBValue, byte deviceId = 0)
{
// 参数校验B值为16位有符号范围-32768~32767
if (calibBValue < short.MinValue || calibBValue > short.MaxValue)
throw new ArgumentOutOfRangeException(nameof(calibBValue), "校准B值必须在-32768~32767之间");
byte[] frame = new byte[7]; // 帧长固定7字节
byte seq = GetCurrentCmdSequence();
ushort bParam = (ushort)calibBValue; // 转换为16位无符号高位在前
// 帧结构ST → Calb+序号 → 设备ID → LTH=7 → 参数1(高8) → 参数2(低8) → 校验和
frame[0] = ST;
frame[1] = (byte)(Calb | seq);
frame[2] = deviceId;
frame[3] = 7; // LTH固定为7
frame[4] = (byte)(bParam >> 8); // 参数1高8位
frame[5] = (byte)(bParam & 0xFF); // 参数2低8位
frame[6] = CalculateChecksum(frame, 6); // 前6字节求和
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 加热温度控制
/// </summary>
public static byte[] SendSetHotTemp(double targetTemp, byte deviceId = 0, bool releaseControl = false)
{
byte[] frame = new byte[7];
byte seq = GetCurrentCmdSequence();
ushort tempParam;
if (releaseControl)
{
tempParam = 0xFFFF; // 释放控制
}
else
{
// 协议范围:室温~100℃对应室温*100~10000
if (targetTemp < 20.0 || targetTemp > 100.0) // 室温暂以20℃为参考下限
throw new ArgumentOutOfRangeException(nameof(targetTemp), "温度必须在[20.0 , 100]℃之间(参考室温)");
tempParam = (ushort)(targetTemp * 100);
}
frame[0] = ST;
frame[1] = (byte)(SETHOTTEMP | seq);
frame[2] = deviceId;
frame[3] = 7; // LTH=7
frame[4] = (byte)(tempParam >> 8);
frame[5] = (byte)(tempParam & 0xFF);
frame[6] = CalculateChecksum(frame, 6);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
/// <summary>
/// 电磁阀控制指令
/// </summary>
public static byte[] SendRelayControl(bool isOpen, byte deviceId = 0)
{
byte[] frame = new byte[6];
byte seq = GetCurrentCmdSequence();
byte param = (byte)(isOpen ? 1 : 0); // 1=开启0=关闭
frame[0] = ST;
frame[1] = (byte)(RelayControl | seq);
frame[2] = deviceId;
frame[3] = 6; // LTH=6
frame[4] = param;
frame[5] = CalculateChecksum(frame, 5);
IncrementCmdSequence();
AddFrameInterval();
return frame;
}
#endregion
#region PC
/// <summary>
/// 解析主控板返回的帧数据(入口方法)
/// </summary>
/// <param name="receivedFrame">主控板返回的原始字节数组</param>
/// <returns>解析结果(结构化对象,包含指令类型、状态、数据)</returns>
public static ParseResult ParseReceivedFrame(byte[] receivedFrame)
{
// 基础校验帧非空、帧长≥5最短帧为5字节
if (receivedFrame == null || receivedFrame.Length < 5)
return new ParseResult(false, "无效帧:数据为空或长度不足", null);
// 1. 校验帧头必须为ST=0x5E
if (receivedFrame[0] != ST)
return new ParseResult(false, $"帧头错误实际0x{receivedFrame[0]:X2}期望0x{ST:X2}", null);
// 2. 提取核心字段
byte cmdWithFlag = receivedFrame[1]; // 带0x80标志的指令码
byte deviceId = receivedFrame[2]; // 设备ID
byte frameLength = receivedFrame[3]; // 帧长LTH
byte checksum = receivedFrame[frameLength - 1]; // 帧中校验和
// 3. 校验帧长(实际长度必须等于帧长字段)
if (receivedFrame.Length != frameLength)
return new ParseResult(false, $"帧长不匹配:实际{receivedFrame.Length}字节,期望{frameLength}字节", null);
// 4. 校验和验证
byte calculatedChecksum = CalculateChecksum(receivedFrame, frameLength - 1);
if (calculatedChecksum != checksum)
return new ParseResult(false, $"校验和错误实际0x{checksum:X2}计算0x{calculatedChecksum:X2}", null);
// 5. 去除响应标志,获取原始指令码
byte originalCmd = (byte)(cmdWithFlag & ~RESPONSE_FLAG);
// 6. 按指令类型分发解析
switch (originalCmd)
{
case HELLO:
return ParseHelloResponse(receivedFrame, deviceId);
case STATUS:
return ParseStatusResponse(receivedFrame, deviceId);
case SETTEMP:
return ParseSetTempResponse(receivedFrame, deviceId);
case MOTOR:
return ParseMotorResponse(receivedFrame, deviceId);
case RDTEMP:
return ParseReadTempResponse(receivedFrame, deviceId);
case RDADC:
return ParseReadAdcResponse(receivedFrame, deviceId);
case RDEE:
return ParseReadEepromResponse(receivedFrame, deviceId);
case WREE:
return ParseWriteEepromResponse(receivedFrame, deviceId);
case RDMotSpeed:
return ParseReadMotorSpeedResponse(receivedFrame, deviceId);
case RDIOStatus:
return ParseReadIoStatusResponse(receivedFrame, deviceId);
case Calb:
return ParseCalibrateTempResponse(receivedFrame, deviceId);
case SETHOTTEMP:
return ParseSetHotTempResponse(receivedFrame, deviceId);
case RelayControl:
return ParseRelayControlResponse(receivedFrame, deviceId);
default:
return new ParseResult(false, $"未知指令0x{originalCmd:X2}", null);
}
}
#region
/// <summary>
/// 解析握手响应
/// </summary>
private static ParseResult ParseHelloResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.1
if (frame.Length != 6)
return new ParseResult(false, "握手响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "握手成功" : $"握手失败(错误码:{status}" }
};
return new ParseResult(true, "握手响应解析成功", data);
}
/// <summary>
/// 解析报错状态响应
/// </summary>
private static ParseResult ParseStatusResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定8字节协议3.2
if (frame.Length != 8)
return new ParseResult(false, "报错状态响应帧长错误需8字节", null);
// 提取Error2字节Bit0-11和状态位
ushort error = (ushort)((frame[4] << 8) | frame[5]);
byte status = frame[6];
// 解析Error按位含义协议3.2
var errorDesc = new Dictionary<string, bool>
{
{ "测温传感器故障Bit0", (error & (1 << 0)) != 0 },
{ "温度过高>200℃Bit1", (error & (1 << 1)) != 0 },
{ "温度过低≤0℃Bit2", (error & (1 << 2)) != 0 },
{ "电机故障Bit3", (error & (1 << 3)) != 0 },
{ "预留Bit4", (error & (1 << 4)) != 0 },
{ "预留Bit5", (error & (1 << 5)) != 0 }
};
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取报错状态成功" : $"读取失败(错误码:{status}" },
{ "报错详情", errorDesc },
{ "原始Error值", $"0x{error:X4}" }
};
return new ParseResult(true, "报错状态响应解析成功", data);
}
/// <summary>
/// 解析温度控制响应
/// </summary>
private static ParseResult ParseSetTempResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.3
if (frame.Length != 6)
return new ParseResult(false, "温度控制响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "温度控制指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "温度控制响应解析成功", data);
}
/// <summary>
/// 解析电机控制响应
/// </summary>
private static ParseResult ParseMotorResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.4
if (frame.Length != 6)
return new ParseResult(false, "电机控制响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "电机控制指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "电机控制响应解析成功", data);
}
/// <summary>
/// 解析读取温度响应
/// </summary>
private static ParseResult ParseReadTempResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定0x0D13字节协议3.5
if (frame.Length != 0x0D)
return new ParseResult(false, $"读取温度响应帧长错误需13字节实际{frame.Length}字节)", null);
// 提取数据设置温度5-6字节、实际温度7-8字节、预留9-10、标志位11、状态位12
ushort setTempParam = (ushort)((frame[4] << 8) | frame[5]);
ushort actualTempParam = (ushort)((frame[6] << 8) | frame[7]);
byte flag = frame[10]; // 0=未到温1=到温
byte status = frame[11];
// 转换为实际温度除以100精度0.1
double setTemp = setTempParam / 100.0;
double actualTemp = actualTempParam / 100.0;
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取温度成功" : $"读取失败(错误码:{status}" },
{ "设置温度(℃)", setTemp },
{ "实际温度(℃)", actualTemp },
{ "到温标志", flag == 0 ? "未到温" : "已到温" },
{ "原始设置温度参数", $"0x{setTempParam:X4}" },
{ "原始实际温度参数", $"0x{actualTempParam:X4}" }
};
return new ParseResult(true, "读取温度响应解析成功", data);
}
/// <summary>
/// 解析读取AD响应
/// </summary>
private static ParseResult ParseReadAdcResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定0x0D13字节协议3.6
if (frame.Length != 0x0D)
return new ParseResult(false, $"读取AD响应帧长错误需13字节实际{frame.Length}字节)", null);
// 提取数据AD值5-6字节、预留7-8、状态位9
ushort adcValue = (ushort)((frame[4] << 8) | frame[5]);
byte status = frame[8];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取AD成功" : $"读取失败(错误码:{status}" },
{ "AD值", adcValue },
{ "原始AD参数", $"0x{adcValue:X4}" }
};
return new ParseResult(true, "读取AD响应解析成功", data);
}
/// <summary>
/// 解析读取EEPROM响应
/// </summary>
private static ParseResult ParseReadEepromResponse(byte[] frame, byte deviceId)
{
// 响应帧长N≥6协议3.7:返回长度-6为数据字节数
if (frame.Length < 6)
return new ParseResult(false, "读取EEPROM响应帧长不足需≥6字节", null);
// 提取数据参数1~参数n5到N-2字节、状态位N-2字节
int dataLength = frame.Length - 6; // 数据字节数N-6
byte[] eepromData = new byte[dataLength];
Array.Copy(frame, 4, eepromData, 0, dataLength); // 从第5字节索引4开始复制
byte status = frame[frame.Length - 2]; // 状态位N-2字节
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "读取EEPROM成功" : $"读取失败(错误码:{status}" },
{ "读取数据长度(字节)", dataLength },
{ "读取数据(十六进制)", BitConverter.ToString(eepromData).Replace("-", " ") },
{ "原始数据数组", eepromData }
};
return new ParseResult(true, "读取EEPROM响应解析成功", data);
}
/// <summary>
/// 解析写入EEPROM响应
/// </summary>
private static ParseResult ParseWriteEepromResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.8
if (frame.Length != 6)
return new ParseResult(false, "写入EEPROM响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "写入EEPROM成功" : $"写入失败(错误码:{status}" }
};
return new ParseResult(true, "写入EEPROM响应解析成功", data);
}
/// <summary>
/// 解析获取实际转速响应
/// </summary>
private static ParseResult ParseReadMotorSpeedResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定8字节协议3.9
if (frame.Length != 8)
return new ParseResult(false, "获取转速响应帧长错误需8字节", null);
// 提取数据转速参数5-6字节、状态位7字节
ushort speedParam = (ushort)((frame[4] << 8) | frame[5]);
byte status = frame[6];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "获取转速成功" : $"获取失败(错误码:{status}" },
{ "实际转速", speedParam },
{ "原始转速参数", $"0x{speedParam:X4}" }
};
return new ParseResult(true, "获取转速响应解析成功", data);
}
/// <summary>
/// 解析获取开关盖状态响应
/// </summary>
private static ParseResult ParseReadIoStatusResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定7字节协议3.10
if (frame.Length != 7)
return new ParseResult(false, "获取开关盖状态响应帧长错误需7字节", null);
// 提取数据开关盖参数5字节、状态位6字节
byte ioParam = frame[4]; // 0=关盖1=开盖
byte status = frame[5];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "获取开关盖状态成功" : $"获取失败(错误码:{status}" },
{ "开关盖状态", ioParam == 0 ? "关盖" : "开盖" },
{ "原始状态参数", $"0x{ioParam:X2}" }
};
return new ParseResult(true, "获取开关盖状态响应解析成功", data);
}
/// <summary>
/// 解析校准温度响应
/// </summary>
private static ParseResult ParseCalibrateTempResponse(byte[] frame, byte deviceId)
{
// 响应帧长固定6字节协议3.11
if (frame.Length != 6)
return new ParseResult(false, "校准温度响应帧长错误需6字节", null);
byte status = frame[4]; // 状态位0=成功)
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "温度校准指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "校准温度响应解析成功", data);
}
/// <summary>
/// 加热温度控制响应解析
/// </summary>
private static ParseResult ParseSetHotTempResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6)
return new ParseResult(false, "加热温度响应帧长错误需6字节", null);
byte status = frame[4];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "加热温度指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "加热温度响应解析成功", data);
}
/// <summary>
/// 电磁阀控制响应解析
/// </summary>
private static ParseResult ParseRelayControlResponse(byte[] frame, byte deviceId)
{
if (frame.Length != 6)
return new ParseResult(false, "电磁阀控制响应帧长错误需6字节", null);
byte status = frame[4];
var data = new Dictionary<string, object>
{
{ "设备ID", deviceId },
{ "状态码", status },
{ "状态描述", status == 0 ? "电磁阀指令接收成功" : $"接收失败(错误码:{status}" }
};
return new ParseResult(true, "电磁阀控制响应解析成功", data);
}
#endregion
#endregion
/// <summary>
/// 帧间隔时间(协议要求>2ms留3ms余量
/// </summary>
private static void AddFrameInterval()
{
Task.Delay(3);
}
/// <summary>
/// CRC16校验用户原类中定义协议未使用保留以备扩展
/// </summary>
public static byte[] CRC16(byte[] data)
{
int len = data.Length;
if (len <= 0)
return new byte[] { 0, 0 };
ushort crc = 0xFFFF;
for (int i = 0; i < len; i++)
{
crc = (ushort)(crc ^ data[i]);
for (int j = 0; j < 8; j++)
{
crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
}
}
byte hi = (byte)((crc & 0xFF00) >> 8);
byte lo = (byte)(crc & 0x00FF);
return BitConverter.IsLittleEndian ? new byte[] { lo, hi } : new byte[] { hi, lo };
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices
{
/// <summary>
/// 解析结果封装(包含成功状态、描述、数据)
/// </summary>
public class ParseResult
{
/// <summary>
/// 解析是否成功
/// </summary>
public bool IsSuccess { get; }
/// <summary>
/// 解析描述(成功/失败原因)
/// </summary>
public string Description { get; }
/// <summary>
/// 解析后的数据Key-Value结构根据指令类型不同内容不同
/// </summary>
public Dictionary<string, object> Data { get; }
public ParseResult(bool isSuccess, string description, Dictionary<string, object> data)
{
IsSuccess = isSuccess;
Description = description;
Data = data ?? new Dictionary<string, object>();
}
}
}

View File

@ -0,0 +1,175 @@
using MegaRobo.Connections;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices.USBScanner
{
public class USBScannerService : ConnectorBase
{
public SerialPortService MegaSerialPort { get; set; }
public bool IsConnected => this.MegaSerialPort?.IsOpen ?? false;
/// <summary>
/// 扫描仪数据接收事件(外部可订阅,实时获取串口数据)
/// </summary>
public event EventHandler<Message> ScannerDataReceived;
#region Base
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
await Task.Delay(0);
this.CoreService = coreService;
if (args != null && args.Length > 0 && args[0] is string portName)
{
this.SetConnection(portName);
}
//else
//{
// throw new ArgumentException("初始化失败未传入有效的串口名称portName");
//}
return string.Empty;
}
private void SetConnection(string portName)
{
// 若已有串口实例,先断开并清理
if (MegaSerialPort != null)
{
Disconnect();
}
this.MegaSerialPort = new SerialPortService(portName);
this.Connection();
}
public string Connection()
{
try
{
if (MegaSerialPort != null && !MegaSerialPort.IsOpen)
{
this.MegaSerialPort.Open();
// 2. 连接成功后订阅串口DataReceived事件先取消再订阅防止重复
MegaSerialPort.DataReceived -= MegaSerialPort_DataReceived; // 取消旧订阅
MegaSerialPort.DataReceived += MegaSerialPort_DataReceived; // 新订阅
}
}
catch (Exception ex)
{
return ex.Message;
}
return this.MegaSerialPort.IsOpen ? null : "连接异常";
}
public Task<string> ConnectionAsync()
{
return Task.Run(this.Connection);
}
public bool Disconnect()
{
if (MegaSerialPort != null)
{
// 3. 断开连接前取消事件订阅(避免内存泄漏)
MegaSerialPort.DataReceived -= MegaSerialPort_DataReceived;
MegaSerialPort.Dispose(); // 触发SerialPortService的Close和资源释放
}
return true;
}
public void Dispose()
{
this.Disconnect();
this.MegaSerialPort = null;
}
#endregion Base
/// <summary>
/// 串口DataReceived事件的处理方法自动触发
/// </summary>
private void MegaSerialPort_DataReceived(object sender, Message msg)
{
if (msg == null || msg.Data == null || msg.Data.Length == 0)
return;
string receivedData = msg.ReadString(SerialPortDataFormat.Char); // 用串口配置的格式解析
// --------------------------
// 步骤2对外透传数据触发自定义事件
// 外部订阅ScannerDataReceived即可实时获取数据
// --------------------------
OnScannerDataReceived(msg);
}
/// <summary>
/// 触发自定义事件(线程安全的事件触发方式)
/// </summary>
protected virtual void OnScannerDataReceived(Message msg)
{
// 临时保存事件引用,避免多线程下事件被注销后为空
var handler = ScannerDataReceived;
handler?.Invoke(this, msg);
}
public string SendAndReceive(string cmd, TimeSpan timeout)
{
string mReply = string.Empty;
EventHandler<Message> tempHandler = (sender, msg) =>
{
mReply = msg.ReadString(SerialPortDataFormat.Char);
};
try
{
MegaSerialPort.DataReceived += tempHandler;
MegaSerialPort.WriteLine(cmd);
Stopwatch stopwatch = Stopwatch.StartNew();
while (string.IsNullOrEmpty(mReply) && stopwatch.Elapsed < timeout)
{
Thread.Sleep(50);
}
}
finally
{
MegaSerialPort.DataReceived -= tempHandler;
}
return mReply;
}
public override async Task<ConnectionState> CheckConnected()
{
await Task.Delay(0);
if (this.MegaSerialPort.IsOpen)
{
return ConnectionState.IsConnected;
}
else
{
return ConnectionState.NotConnected;
}
}
public override async Task<ConnectionState> ReconnectAsync()
{
await Task.Delay(0);
if (string.IsNullOrEmpty(await ConnectionAsync()))
{
return ConnectionState.IsConnected;
}
else
{
return ConnectionState.NotConnected;
}
}
}
}

View File

@ -0,0 +1,163 @@
using MegaRobo.Connections;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.ControlDevices.WeightDevice
{
public class WeightService_MT : ConnectorBase<MegaTcpClient>
{
public event EventHandler<TcpMessage> DataReceived;
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
this.CoreService = coreService;
try
{
base.CoreService = coreService;
if (args is [IPEndPoint remoteHost, ..])
{
base.ConnectorAdapter = new MegaTcpClient
{
Logger = coreService.Logger,
Name = base.Name
};
if (base.ConnectorAdapter != null)
{
base.ConnectorAdapter.DataReceived += ConnectorOnDataReceived;
}
ConnectionState = await base.ConnectorAdapter.ConnectAsync(remoteHost);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
return await Task.FromResult(ex.Message);
}
return string.Empty;
}
private void ConnectorOnDataReceived(object sender, TcpMessage tcpMsg)
{
if (tcpMsg is null)
return;
var msg = tcpMsg.ParseStirng();
if (string.IsNullOrEmpty(msg)) return;
this.Logger.LogInformation($"接收到梅特勒称重仪_{this.Name}数据:{msg?.Trim()}");
this.DataReceived?.Invoke(this, tcpMsg);
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
/// <summary>
/// 重置清零
/// </summary>
/// <param name="immediatly">是否立即,否:使用采用当前稳定重量值作为零点</param>
/// <returns></returns>
public bool Reset(bool immediately = true)
{
try
{
string command = immediately ? "ZI" : "Z";
ConnectorAdapter.WriteLine(command);
}
catch (Exception ex)
{
base.Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService", "Reset", 150, false);
}
return true;
}
/// <summary>
/// 去皮
/// </summary>
/// <param name="immediately"></param>
/// <returns></returns>
public bool Peel(bool immediately = true)
{
try
{
string command = immediately ? "TI" : "T";
ConnectorAdapter.WriteLine(command);
}
catch (Exception ex)
{
base.Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService", "Peel", 150, false);
}
return true;
}
public bool ClearPeel()
{
try
{
string command = "TAC";
ConnectorAdapter.WriteLine(command);
}
catch (Exception ex)
{
base.Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService", "ClearPeel", 150, false);
}
return true;
}
/// <summary>
/// 读取称重值
/// </summary>
/// <param name="timeout"></param>
/// <param name="immediately"></param>
/// <returns></returns>
public async Task<string> ReadWeightAsnyc(TimeSpan timeout, bool immediately = true)
{
try
{
string cmd = immediately ? "SI\r\n" : "S\r\n";
byte[] sendBytes = ConnectorAdapter.StringEncoder.GetBytes(cmd);
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(sendBytes, timeout);
if (tcpMessage is null)
return string.Empty;
var result = tcpMessage.ParseStirng();
//"S S 0.0000 g\r\n"
//"S D -0.0000 g\r\n"
var match = Regex.Match(result, @"-?[0-9]+(\.[0-9]{1,4})");
return match.Success ? match.Groups[0].Value : "";
//这边是不是要加个校验 S d 像你写的那样 无所谓的 因为 能读到就行 我看你的 引用地方 也没有对 非重量数值做处理
}
catch (Exception ex)
{
base.Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService", "ReadWeightAsnyc", 150, false);
}
return string.Empty;
}
/// <summary>
/// 检测连接状态
/// </summary>
/// <returns></returns>
public override async Task<ConnectionState> CheckConnected()
{
var state = ConnectionState.None;
if (ConnectorAdapter is not null)
{
state = await ConnectorAdapter.CheckConnectedAsync();
}
ConnectionState = state;
return state;
}
}
}

View File

@ -0,0 +1,243 @@
using MegaRobo.Connections;
using MegaRobo.Connections.Modbus;
using MegaRobo.Connections.Sockets.Tcp;
using MegaRobo.Contract;
using MegaRobo.ControlDevices.Abstractions;
using MegaRobo.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static MegaRobo.C00225155.ControlDevices.HighTemperatureMotorControlService;
using static MegaRobo.Connections.Modbus.ModbusParams;
namespace MegaRobo.C00225155.ControlDevices
{
public class WeightService_Ts02 : ConnectorBase<MegaTcpClient>
{
/// <summary>
/// 寄存器地址枚举
/// </summary>
private enum MotorRegisterAddress : ushort
{
WeightValueLow = 0x0000,
WeightValueHigh = 0x0002,
/// <summary>
/// 1校正 2置零
/// </summary>
Command = 0x0007,
/// <summary>
/// 1去皮
/// </summary>
Peel = 0x000B,
/// <summary>
/// 波特率 19200
/// </summary>
BuartRate = 0x0010,
Decimalpos = 0x0014,
}
public override async Task<string> Initialize(ICoreService coreService, params object[] args)
{
CoreService = coreService;
try
{
base.CoreService = coreService;
if (args is [IPEndPoint remoteHost, ..])
{
base.ConnectorAdapter = new MegaTcpClient
{
Logger = coreService.Logger,
Name = base.Name
};
if (base.ConnectorAdapter != null)
{
base.ConnectorAdapter.DataReceived += ConnectorOnDataReceived;
}
ConnectionState = await base.ConnectorAdapter.ConnectAsync(remoteHost);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
return await Task.FromResult(ex.Message);
}
return string.Empty;
}
private void ConnectorOnDataReceived(object sender, TcpMessage tcpMsg)
{
if (tcpMsg is null || tcpMsg.Data == null || tcpMsg.Data.Length == 0)
return;
string hexData = BitConverter.ToString(tcpMsg.Data).Replace("-", " ");
}
public override Task<ConnectionState> ReconnectAsync()
{
return base.ReconnectAsync();
}
public override async Task<ConnectionState> CheckConnected()
{
var state = ConnectionState.None;
if (ConnectorAdapter is not null)
{
state = await ConnectorAdapter.CheckConnectedAsync();
}
ConnectionState = state;
return state;
}
/// <summary>
/// 去皮
/// </summary>
/// <returns></returns>
public async Task<bool> Peel()
{
try
{
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: 4, // 从站地址默认1
startAdress: (ushort)MotorRegisterAddress.Peel,
lstData: (ushort)1,
byteOrder: ByteOrder.AB // 协议要求高字节在前
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"去皮失败:{writeResult.Message}");
return false;
}
ConnectorAdapter.Write(writeResult.Respond);
return true;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService_Ts02", "Peel", 150, false);
return false;
}
}
/// <summary>
/// 置零
/// </summary>
/// <returns></returns>
public async Task<bool> ResetZero()
{
try
{
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: 4, // 从站地址默认1
startAdress: (ushort)MotorRegisterAddress.Command,
lstData: (ushort)2,
byteOrder: ByteOrder.AB
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"置零失败:{writeResult.Message}");
return false;
}
ConnectorAdapter.Write(writeResult.Respond);
return true;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService_Ts02", "ResetZero", 150, false);
return false;
}
}
/// <summary>
/// 读取称的值
/// </summary>
/// <returns></returns>
public async Task<double> ReadWeightValue()
{
try
{
var readResult = ModbusRTU.GetReadMessage(
slaveStation: 4,
readType: ModbusReadCodes.READ_HOLDING_REGISTERS, // 功能码03
startAdr: (ushort)MotorRegisterAddress.WeightValueLow,
length: 2 // 状态寄存器是1个16位寄存器长度=1
);
if (readResult.IsSuccessed)
{
//FF 03 00 14 00 01 D1 D0 => 04 03 02 00 01 B5 84
var digitCountMessage = await ConnectorAdapter.WriteAndGetReplyAsync(new byte[] { 0xFF, 0x03, 0x00, 0x14, 0x00, 0x01, 0xD1, 0xD0 }, TimeSpan.FromSeconds(5));
if (digitCountMessage == null || digitCountMessage.Data == null)
{
return 0;
}
var digitCount = ModbusRTU.ReadRegisterShort(digitCountMessage.Data, ByteOrder.AB).Respond[0];
var tcpMessage = await ConnectorAdapter.WriteAndGetReplyAsync(readResult.Respond, TimeSpan.FromSeconds(5));
if (tcpMessage == null || tcpMessage.Data == null)
{
return 0;
}
var parseResult = ModbusRTU.ReadRegisterShort(tcpMessage.Data, ByteOrder.AB);
if (!parseResult.IsSuccessed)
{
Logger.LogError($"状态解析失败:{parseResult.Message}");
return 0;
}
var res = ((int)parseResult.Respond[0] | ((int)parseResult.Respond[1] << 16)) / (Math.Pow(10, digitCount));
return res;
//return parseResult.Respond[0] / 10.0;
}
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService_Ts02", "ReadWeightValue", 150, false);
return 0;
}
return 0;
}
/// <summary>
/// 设置小数点位数
/// </summary>
/// <param name="value">0~5</param>
/// <returns></returns>
public async Task<bool> SetDecimalPoint(ushort value)
{
if (value < 0 || value > 5)
{
return false;
}
try
{
var writeResult = ModbusRTU.WriteRegisterSingle(
deviceAdress: 4, // 从站地址默认1
startAdress: (ushort)MotorRegisterAddress.Decimalpos,
lstData: value,
byteOrder: ByteOrder.AB
);
if (!writeResult.IsSuccessed)
{
Logger.LogError($"写小数点位数失败:{writeResult.Message}");
return false;
}
ConnectorAdapter.Write(writeResult.Respond);
return true;
}
catch (Exception ex)
{
Logger.LogException(ex, null, LogEvent.Error, null, LogLevel.Error, "WeightService_Ts02", "SetDecimalPoint", 150, false);
return false;
}
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using MegaRobo.DataAccess;
using System.Linq;
using MegaRobo.Entities.ViewTT;
using MegaRobo.C00225155.Entities.Entity_DB;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Collections;
using System.DirectoryServices.Protocols;
namespace MegaRobo.C00225155.DataAccess;
public class DataAccessService : DataAccessBase, IDataAccessService
{
#region Labx
#endregion
public async Task<(bool, string)> CreateLiquid1mlPipetteProductModel(Liquid_lml_Dose liquidmodel)
{
(bool isOk, string msg) = (false, "");
try
{
using var ctx = this.GetEntities();
var modelId = await ctx.Insertable(liquidmodel).ExecuteReturnEntityAsync();
isOk = true;
}
catch (Exception ex)
{
this.OnException(ex);
msg = ex.Message;
}
return (isOk, msg);
}
public async Task<(bool, string)> CreateLiquid1mlPipetteProductModel_React(Liquid_lml_React liquidmodel)
{
(bool isOk, string msg) = (false, "");
try
{
using var ctx = this.GetEntities();
var modelId = await ctx.Insertable(liquidmodel).ExecuteReturnEntityAsync();
isOk = true;
}
catch (Exception ex)
{
this.OnException(ex);
msg = ex.Message;
}
return (isOk, msg);
}
#region
#endregion
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using MegaRobo.Contract;
using MegaRobo.DataAccess;
using MegaRobo.Logger.Loggers.Database;
using MegaRobo.Entities.ViewTT;
using MegaRobo.C00225155.Entities.Entity_DB;
namespace MegaRobo.C00225155.DataAccess;
public interface IDataAccessService : IDataAccess, IEntitiesAccess, ILoggerDataService, IUserDataService
{
#region Labx
#endregion
#region
Task<(bool, string)> CreateLiquid1mlPipetteProductModel(Liquid_lml_Dose liquidmodel);
Task<(bool, string)> CreateLiquid1mlPipetteProductModel_React(Liquid_lml_React liquidmodel);
#endregion
}

View File

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<Reference Include="MegaRobo.Authentication">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Authentication.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Contract">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Contract.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.DataAccess">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.DataAccess.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Entities">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Entities.dll</HintPath>
</Reference>
<Reference Include="MegaRobo.Logger">
<HintPath>..\..\..\Common\Published\NET7.0\MegaRobo.Logger.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.110" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MegaRobo.C00225155.Entities\MegaRobo.C00225155.Entities.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,77 @@
using System.ComponentModel;
namespace MegaRobo.C00225155.Entities;
public enum DeviceNames
{
/// <summary>
///投料站预称重
/// </summary>
PreWeight,
/// <summary>
/// 投料站梅特勒粉末称重模块
/// </summary>
PowderWeight,
/// <summary>
/// 投料站扫码枪
/// </summary>
Scanner,
/// <summary>
/// 投料站粉末扫码枪
/// </summary>
Scanner_Powder,
/// <summary>
///
/// </summary>
Scanner_React,
/// <summary>
/// 反应站料仓架扫码枪
/// </summary>
Scanner_ReactRack,
/// <summary>
/// 投料站移液枪
/// </summary>
Pipette_Dose_1ml,
/// <summary>
/// 投料站 id=1移液枪
/// </summary>
Pipette_Dose_1ml_1,
/// <summary>
/// 投料站id=2移液枪
/// </summary>
Pipette_Dose_1ml_2,
/// <summary>
/// 投料站id=3移液枪
/// </summary>
Pipette_Dose_1ml_3,
/// <summary>
/// 投料站id=4移液枪
/// </summary>
Pipette_Dose_1ml_4,
/// <summary>
/// 反应站1ml移液器
/// </summary>
Pipette_React_1ml,
/// <summary>
/// 投料站PLC
/// </summary>
Plc1,
/// <summary>
/// 反应站PLC
/// </summary>
Plc2,
/// <summary>
/// 高温震荡
/// </summary>
HightTemperatureShake,
/// <summary>
/// 高温震荡电机
/// </summary>
HightTemperatureMotor,
/// <summary>
/// 低温磁力搅拌
/// </summary>
LowTemperatureMagneticStir,
}

View File

@ -0,0 +1,204 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
public class DataResult_Dose:ObservableObject
{
private int boxId;
/// <summary>
/// 样品瓶工装号
/// </summary>
public int BoxId
{
get => boxId;
set => SetProperty(ref boxId, value);
}
private int bottleId;
/// <summary>
/// 样品瓶号
/// </summary>
public int BottleId
{
get => bottleId;
set => SetProperty(ref bottleId, value);
}
private string sampleCode;
/// <summary>
/// 样品瓶SN码
/// </summary>
public string SampleCode
{
get => sampleCode;
set => SetProperty(ref sampleCode, value);
}
private ObservableCollection<AddLiquidData_Dose> liquidAdditives = new ObservableCollection<AddLiquidData_Dose>();
public ObservableCollection<AddLiquidData_Dose> LiquidAdditives
{
get => liquidAdditives;
set => SetProperty(ref liquidAdditives, value);
}
private ObservableCollection<AddPowderData_Dose> powderAdditives = new ObservableCollection<AddPowderData_Dose>();
public ObservableCollection<AddPowderData_Dose> PowderAdditives
{
get => powderAdditives;
set => SetProperty(ref powderAdditives, value);
}
}
public class AddLiquidData_Dose : ObservableObject
{
private string sampleCode;
/// <summary>
/// SN码
/// </summary>
public string SampleCode
{
get => sampleCode;
set => SetProperty(ref sampleCode, value);
}
private string liquidname;
/// <summary>
/// 液体名称
/// </summary>
public string LiquidName
{
get => liquidname;
set => SetProperty(ref liquidname, value);
}
private double targetVolume;
/// <summary>
/// 目标加入体积
/// </summary>
public double TargetVolume
{
get => targetVolume;
set => SetProperty(ref targetVolume, value);
}
private double actualAddVolume;
/// <summary>
/// 实际加入体积
/// </summary>
public double ActualAddVolume
{
get => actualAddVolume;
set => SetProperty(ref actualAddVolume, value);
}
private int addState;
/// <summary>
/// 0表示还未增加 1表示正在增加 2表示增加完成
/// </summary>
public int AddState
{
get => addState;
set => SetProperty(ref addState, value);
}
private string precision;
/// <summary>
/// 精度
/// </summary>
public string Precision
{
get => precision;
set => SetProperty(ref precision, value);
}
private bool result;
/// <summary>
/// 检测结果true合格false不合格
/// </summary>
public bool bOK
{
get => result;
set => SetProperty(ref result, value);
}
}
public class AddPowderData_Dose : ObservableObject
{
private string sampleCode;
/// <summary>
/// SN码
/// </summary>
public string SampleCode
{
get => sampleCode;
set => SetProperty(ref sampleCode, value);
}
private string powdername;
/// <summary>
/// 粉末名称
/// </summary>
public string PowderName
{
get => powdername;
set => SetProperty(ref powdername, value);
}
private double targetWeight;
/// <summary>
/// 目标加入量
/// </summary>
public double TargetWeight
{
get => targetWeight;
set => SetProperty(ref targetWeight, value);
}
private double actualAddWeight;
/// <summary>
/// 实际加入量
/// </summary>
public double ActualAddWeight
{
get => actualAddWeight;
set => SetProperty(ref actualAddWeight, value);
}
private int addState;
/// <summary>
/// 0表示还未增加 1表示正在增加 2表示增加完成
/// </summary>
public int AddState
{
get => addState;
set => SetProperty(ref addState, value);
}
private string precision;
/// <summary>
/// 精度
/// </summary>
public string Precision
{
get => precision;
set => SetProperty(ref precision, value);
}
private bool result;
/// <summary>
/// 检测结果true合格false不合格
/// </summary>
public bool bOK
{
get => result;
set => SetProperty(ref result, value);
}
}
}

View File

@ -0,0 +1,143 @@
using CommunityToolkit.Mvvm.ComponentModel;
using MegaRobo.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
[Table(Comment = "反应站1ml移液枪吸液配置", Name = "Liquid_lml_React")]
public class Liquid_lml_React
{
/// <summary>
/// 自增ID
/// </summary>
[Column(IsIdentity = true, IsPrimaryKey = true, Comment = "自增ID")]
public int Id { get; set; }
/// <summary>
/// 150ul 2300ul 31000ul
/// </summary>
public int TipType { set; get; }
/// <summary>
/// 开盖取样/稀释加样
/// </summary>
public string FuncType { get; set; }
/// <summary>
/// 瓶子类型 112ml样品瓶 25ml样品瓶
/// </summary>
public int BottleType { set; get; }
/// <summary>
/// 液体名称
/// </summary>
public string LiquidName { get; set; }
/// <summary>
/// 以纯水为标准的吸取比例
/// </summary>
public double Ratio { get; set; } = 1.0000;
/// <summary>
/// 液体编号
/// </summary>
public int LiquidParamId { get; set; }
/// <summary>
/// 容器编号
/// </summary>
public int ContainerPlateIndex { get; set; }
/// <summary>
/// 平台编号
/// </summary>
public int PlatformCode { get; set; }
/// <summary>
/// 是否液面探测
/// </summary>
public int LiqLedetec { get; set; }
/// <summary>
/// 吸液高度
/// </summary>
public int AspireHeight { get; set; }
/// <summary>
/// 分液高度
/// </summary>
public int DispenHeight { get; set; }
/// <summary>
/// 液体体积/
/// </summary>
public int LiquidVolume { get; set; }
}
public class Liquid_lml_React_Show : ObservableObject
{
private int id;
public int Id { get => id; set => SetProperty(ref id, value); }
private int tiptype;
/// <summary>
/// 150ul 2300ul 31000ul
/// </summary>
public int TipType { get => tiptype; set => SetProperty(ref tiptype, value); }
private string functype;
/// <summary>
/// 用途,开盖取样/稀释加样
/// </summary>
public string FuncType { get => functype; set => SetProperty(ref functype, value); }
private int bottletype;
/// <summary>
/// 瓶子类型 112ml样品瓶 25ml样品瓶
/// </summary>
public int BottleType { get => bottletype; set => SetProperty(ref bottletype, value); }
private string liquidname;
/// <summary>
/// 液体名称
/// </summary>
public string LiquidName { get => liquidname; set => SetProperty(ref liquidname, value); }
private double ratio = 1.0000;
public double Ratio { get => ratio; set => SetProperty(ref ratio, value); }
private int liquidparamid;
/// <summary>
/// 液体编号
/// </summary>
public int LiquidParamId { get => liquidparamid; set => SetProperty(ref liquidparamid, value); }
private int containerPlateIndex;
/// <summary>
/// 容器编号
/// </summary>
public int ContainerPlateIndex { get => containerPlateIndex; set => SetProperty(ref containerPlateIndex, value); }
private int plateformcode;
/// <summary>
/// 平台编号
/// </summary>
public int PlatformCode { get => plateformcode; set => SetProperty(ref plateformcode, value); }
private int liqLedetec;
/// <summary>
/// 是否液面探测
/// </summary>
public int LiqLedetec { get => liqLedetec; set => SetProperty(ref liqLedetec, value); }
private int aspireHeight;
/// <summary>
/// 吸液高度
/// </summary>
public int AspireHeight { get => aspireHeight; set => SetProperty(ref aspireHeight, value); }
private int dispenHeight;
/// <summary>
/// 分液高度
/// </summary>
public int DispenHeight { get => dispenHeight; set => SetProperty(ref dispenHeight, value); }
private int liquidVolume;
/// <summary>
/// 液体体积/
/// </summary>
public int LiquidVolume { get => liquidVolume; set => SetProperty(ref liquidVolume, value); }
}
}

View File

@ -0,0 +1,136 @@
using CommunityToolkit.Mvvm.ComponentModel;
using MegaRobo.Entities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
[Table(Comment = "投料站1ml移液枪吸液配置", Name = "Liquid_lml_Dose")]
public class Liquid_lml_Dose
{
/// <summary>
/// 自增ID
/// </summary>
[Column(IsIdentity = true, IsPrimaryKey = true, Comment = "自增ID")]
public int Id { get; set; }
/// <summary>
/// 150ul 2300ul 31000ul
/// </summary>
public int TipType { set; get; }
///// <summary>
///// 瓶子类型 112ml样品瓶 25ml样品瓶
///// </summary>
//public int BottleType { set; get; }
/// <summary>
/// 液体名称
/// </summary>
public string LiquidName { get; set; }
/// <summary>
/// 以纯水为标准的吸取比例
/// </summary>
public double Ratio { get; set; } = 1.0000;
/// <summary>
/// 液体编号
/// </summary>
public int LiquidParamId { get; set; }
/// <summary>
/// 吸液高度
/// </summary>
public int AspireHeight { get; set; }
/// <summary>
/// 分液高度
/// </summary>
public int DispenHeight { get; set; }
/// <summary>
/// 容器编号
/// </summary>
public int ContainerPlateIndex { get; set; }
/// <summary>
/// 平台编号
/// </summary>
public int PlatformCode { get; set; }
/// <summary>
/// 是否液面探测
/// </summary>
public int LiqLedetec { get; set; }
/// <summary>
/// 液体体积/
/// </summary>
public int LiquidVolume { get; set; }
}
public class Liquid_lml_Dose_Show : ObservableObject
{
private int id;
public int Id { get => id; set => SetProperty(ref id, value); }
private int tiptype;
/// <summary>
/// 150ul 2300ul 31000ul
/// </summary>
public int TipType { get => tiptype; set => SetProperty(ref tiptype, value); }
//private int bottletype;
///// <summary>
///// 瓶子类型 112ml样品瓶 25ml样品瓶
///// </summary>
//public int BottleType { get => bottletype; set => SetProperty(ref bottletype, value); }
private string liquidname;
/// <summary>
/// 液体名称
/// </summary>
public string LiquidName { get => liquidname; set => SetProperty(ref liquidname, value); }
private double ratio = 1.0000;
public double Ratio { get => ratio; set => SetProperty(ref ratio, value); }
private int liquidparamid;
/// <summary>
/// 液体编号
/// </summary>
public int LiquidParamId { get => liquidparamid; set => SetProperty(ref liquidparamid, value); }
private int containerPlateIndex;
/// <summary>
/// 容器编号
/// </summary>
public int ContainerPlateIndex { get => containerPlateIndex; set => SetProperty(ref containerPlateIndex, value); }
private int plateformcode;
/// <summary>
/// 平台编号
/// </summary>
public int PlatformCode { get => plateformcode; set => SetProperty(ref plateformcode, value); }
private int liqLedetec;
/// <summary>
/// 是否液面探测
/// </summary>
public int LiqLedetec { get => liqLedetec; set => SetProperty(ref liqLedetec, value); }
private int aspireHeight;
/// <summary>
/// 吸液高度
/// </summary>
public int AspireHeight { get => aspireHeight; set => SetProperty(ref aspireHeight, value); }
private int dispenHeight;
/// <summary>
/// 分液高度
/// </summary>
public int DispenHeight { get => dispenHeight; set => SetProperty(ref dispenHeight, value); }
private int liquidVolume;
/// <summary>
/// 液体体积/
/// </summary>
public int LiquidVolume { get => liquidVolume; set => SetProperty(ref liquidVolume, value); }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
/// <summary>
/// PLC 错误信息点位
/// </summary>
public class PLCErrorPoint
{
public string ErrorAddress { get; set; }
public string ErrorInfo { get; set; }
}
}

View File

@ -0,0 +1,35 @@
using MegaRobo.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
[Table(Comment = "PLC报警点位信息 ", Name = "PlcPointDict")]
public class PlcPointDict
{
/// <summary>
/// 唯一的Guid
/// </summary>
[Column(IsPrimaryKey = true)]
public string Guid { get; set; }
public string Name { get; set; }
public string Key { get; set; }
public short Address { get; set; }
public string ValueTypeName { get; set; }
public int FunType { get; set; }
public int Level { get; set; }
public string Antidote { get; set; }
public string Descr { get; set; }
}
}

View File

@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
/// <summary>
///
/// </summary>
public static class PlcPoints_MaterialDose
{
/// <summary>
/// 1s周期 0和1切换
/// </summary>
public readonly static ushort R_PLCHeartPluse = 3000;
/// <summary>
/// 0None 1手动 2自动
/// </summary>
public readonly static ushort R_PLCMode = 3001;
/// <summary>
/// 0None 1:停止 2运行中
/// </summary>
public readonly static ushort R_PLCRunStatus = 3002;
/// <summary>
/// 初始化状态 0None 1初始化中 2初始化失败 3初始化完成
/// </summary>
public readonly static ushort R_PLCInitialStatus = 3003;
/// <summary>
/// 消息状态 0无消息 1有消息
/// </summary>
public readonly static ushort R_PLCInfoStatus = 3004;
/// <summary>
/// 处理中任务队列序号
/// </summary>
public readonly static ushort R_TransferPlate_inProcessingTaskNo = 3100;
/// <summary>
/// 0需初始化
/// 1硬件故障中
/// 2初始化中
/// 3初始化失败
/// 4功能完全屏蔽中
/// 5准备就绪空闲中
/// 6与其他单元任务冲突自主去往避让位置中
/// 10此任务与其他单元任务冲突是否继续等待
/// 11运行上位机任务中
/// 12运行上位机任务中但在等待其他单元到避让位置
/// 15运行上位机任务中部分功能屏蔽
/// 16任务暂停中
/// 21失败
/// 22完成
/// 99任务撤销
/// </summary>
public readonly static ushort R_TransferPlate_RunResult = 3101;
public readonly static ushort R_TransferPlate_BoxPos = 3102;
/// <summary>
/// 机械手处理中任务队列序号
/// </summary>
public static ushort R_Robot_inProcessingTaskNo = 3200;
/// <summary>
/// 机械臂执行结果
/// </summary>
public readonly static ushort R_Robot_RunResult = 3201;
public readonly static ushort R_MetAddPowderModuleDoorState = 3220;
public readonly static ushort R_Robot_ArriveScanPos = 3221;
public readonly static ushort R_Robot_ArriveSideScanPos = 3222;
public readonly static ushort R_Robot_ArrivePreWeightPos = 3223;
/// <summary>
/// 开关盖&移液枪处理中任务队列序号
/// </summary>
public readonly static ushort R_BottleLidAndPipette_inProcessingTaskNo = 3300;
/// <summary>
/// 开关盖&移液执行结果
/// </summary>
public readonly static ushort R_BottleLidAndPipette_RunResult = 3301;
/// <summary>
/// 1ml移液枪Z轴初始化
/// </summary>
public readonly static ushort R_1mlPipetteZInit = 3320;
/// <summary>
/// 1ml移液枪初始化
/// </summary>
public readonly static ushort R_1mlPipetteInit = 3321;
public readonly static Dictionary<ushort, ushort> W_PCHeartPluse = new Dictionary<ushort, ushort> {
{ (ushort)DeviceNames.Plc1, 2000 }, { (ushort)DeviceNames.Plc2, 2000 }};
/// <summary>
/// 模式切换 间隔200ms
/// </summary>
public readonly static ushort W_PCMode = 2001;
/// <summary>
/// 1:停止 2运行中
/// </summary>
public readonly static ushort W_PCRunStatus = 2002;
/// <summary>
/// 整机初始化
/// </summary>
public readonly static ushort W_Initial = 2003;
/// <summary>
/// 消息确认
/// </summary>
public readonly static ushort W_InformationConfirm = 2004;
/// <summary>
/// 上位机有报警提示 0无 1用于触发蜂鸣器响
/// </summary>
public readonly static ushort W_PCErrorTip = 2008;
/// <summary>
/// 上位机有警告提示 0无 1用于触发蜂鸣器响
/// </summary>
public readonly static ushort W_PCAlarmTip = 2009;
/// <summary>
/// 传送平台任务队列序号
/// </summary>
public readonly static ushort W_TransferPlate_TaskQueue = 2100;
/// <summary>
/// 传送平台任务号
/// </summary>
public readonly static ushort W_TransferPlate_TaskNo = 2101;
/// <summary>
/// 传送平台任务撤销
/// </summary>
public readonly static ushort W_TransferPlate_TaskCancel = 2199;
/// <summary>
/// 机械臂任务队列序号
/// </summary>
public readonly static ushort W_Robot_TaskQueue = 2200;
/// <summary>
/// 机械臂任务号
/// </summary>
public readonly static ushort W_Robot_TaskNo = 2201;
/// <summary>
/// 目标区域
/// 1手套箱传送平台 2Tip托盘存放平台12托盘存放平台
/// </summary>
public readonly static ushort W_Robot_Param1 = 2202;
/// <summary>
/// 目标托盘位置号
/// </summary>
public readonly static ushort W_Robot_Param2 = 2203;
/// <summary>
/// 托盘类型1:40mL瓶; 2:12mL瓶; 3:5mL瓶; 4:TIP头;
/// </summary>
public readonly static ushort W_Robot_Param3 = 2204;
public readonly static ushort W_Robot_Param4 = 2205;
public readonly static ushort W_Robot_Param5 = 2206;
/// <summary>
/// 扫码完成 1ok 2:ng
/// </summary>
public readonly static ushort W_Robot_ScanResult = 2221;
/// <summary>
/// 侧面扫码完成 1ok 2:ng
/// </summary>
public readonly static ushort W_Robot_SideScanResult = 2222;
/// <summary>
/// 预称重完成
/// </summary>
public readonly static ushort W_Robot_PreWeightFinish = 2223;
/// <summary>
/// 机械臂任务撤销
/// </summary>
public readonly static ushort W_Robot_TaskCancel = 2299;
/// <summary>
/// 开关盖&移液枪 任务队列序号
/// </summary>
public readonly static ushort W_BottleLidAndPipette_TaskQueue = 2300;
/// <summary>
/// 开关盖&移液枪任务号
/// </summary>
public readonly static ushort W_BottleLidAndPipette_TaskNo = 2301;
public readonly static ushort W_BottleLidAndPipette_Param1 = 2302;
public readonly static ushort W_BottleLidAndPipette_Param2 = 2303;
public readonly static ushort W_BottleLidAndPipette_Param3 = 2304;
public readonly static ushort W_BottleLidAndPipette_Param4 = 2305;
/// <summary>
/// 1ml移液枪Z轴初始化结果 1OK 2:NG
/// </summary>
public readonly static ushort W_1mlPipetteZInitResult = 2320;
/// <summary>
/// 1ml移液枪初始化结果 1OK 2:NG
/// </summary>
public readonly static ushort W_1mlPipetteInitResult = 2321;
/// <summary>
/// 开关盖&移液枪任务撤销
/// </summary>
public readonly static ushort W_BottleLidAndPipette_TaskCancel = 2399;
}
}

View File

@ -0,0 +1,258 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Entity_DB
{
public static class PlcPoints_React
{
/// <summary>
/// 1s周期 0和1切换
/// </summary>
public readonly static ushort R_PLCHeartPluse = 3000;
/// <summary>
/// 0None 1手动 2自动
/// </summary>
public readonly static ushort R_PLCMode = 3001;
/// <summary>
/// 0None 1:停止 2运行中
/// </summary>
public readonly static ushort R_PLCRunStatus = 3002;
/// <summary>
/// 初始化状态 0None 1初始化中 2初始化失败 3初始化完成
/// </summary>
public readonly static ushort R_PLCInitialStatus = 3003;
/// <summary>
/// 消息状态 0无消息 1有消息
/// </summary>
public readonly static ushort R_PLCInfoStatus = 3004;
/// <summary>
/// 旋转料仓处理中任务队列序号
/// </summary>
public readonly static ushort R_RotatorRack_inProcessingTaskNo = 3100;
/// <summary>
/// 0需初始化
/// 1硬件故障中
/// 2初始化中
/// 3初始化失败
/// 4功能完全屏蔽中
/// 5准备就绪空闲中
/// 6与其他单元任务冲突自主去往避让位置中
/// 10此任务与其他单元任务冲突是否继续等待
/// 11运行上位机任务中
/// 12运行上位机任务中但在等待其他单元到避让位置
/// 15运行上位机任务中部分功能屏蔽
/// 16任务暂停中
/// 21失败
/// 22完成
/// 99任务撤销
/// </summary>
public readonly static ushort R_RotatorRack_RunResult = 3101;
/// <summary>
/// 进出托盘处理中任务队列序号
/// </summary>
public static ushort R_InOutPlate_inProcessingTaskNo = 3200;
/// <summary>
/// 进出托盘执行结果
/// </summary>
public readonly static ushort R_InOutPlate_RunResult = 3201;
/// <summary>
/// 龙门移载处理中任务队列序号
/// </summary>
public readonly static ushort R_GantryTransfer_inProcessingTaskNo = 3300;
/// <summary>
/// 开关盖&移液执行结果
/// </summary>
public readonly static ushort R_GantryTransfer_RunResult = 3301;
/// <summary>
/// 1ml移液枪Z轴初始化
/// </summary>
public readonly static ushort R_1mlPipetteZInit = 3320;
/// <summary>
/// 1ml移液枪初始化
/// </summary>
public readonly static ushort R_1mlPipetteInit = 3321;
/// <summary>
/// 允许上外瓶
/// </summary>
public readonly static ushort R_UploadOuterBottle = 3322;
/// <summary>
/// 允许上滤芯
/// </summary>
public readonly static ushort R_UploadInnerBottle = 3323;
/// <summary>
/// 允许加样本或者稀释剂
/// </summary>
public readonly static ushort R_AddSampleOrDiluent = 3324;
/// <summary>
/// 磁力搅拌处理中任务队列序号
/// </summary>
public static ushort R_LowTemperatureMagneticStir_inProcessingTaskNo = 3400;
/// <summary>
/// 磁力搅拌执行结果
/// </summary>
public readonly static ushort R_LowTemperatureMagneticStir_RunResult = 3401;
/// <summary>
/// 磁力搅拌器1 盖子状态 1打开 2关闭
/// </summary>
public readonly static ushort R_LowTemperatureMagneticStir_LidState1 = 3420;
/// <summary>
/// 磁力搅拌器2 盖子状态 1打开 2关闭
/// </summary>
public readonly static ushort R_LowTemperatureMagneticStir_LidState2 = 3421;
/// <summary>
/// 氮吹 处理中任务队列序号
/// </summary>
public readonly static ushort R_NitrogenBlowing__inProcessingTaskNo = 3500;
/// <summary>
/// 氮吹运行结果
/// </summary>
public readonly static ushort R_NitrogenBlowing_RunResult = 3501;
/// <summary>
/// 氮吹 上盖状态 1:打开 2关闭
/// </summary>
public readonly static ushort R_NitrogenBlowing_LidState = 3550;
public readonly static Dictionary<ushort, ushort> W_PCHeartPluse = new Dictionary<ushort, ushort> {
{ (ushort)DeviceNames.Plc1, 2000 }, { (ushort)DeviceNames.Plc2, 2000 }};
/// <summary>
/// 模式切换 间隔200ms
/// </summary>
public readonly static ushort W_PCMode = 2001;
/// <summary>
/// 1:停止 2运行中
/// </summary>
public readonly static ushort W_PCRunStatus = 2002;
/// <summary>
/// 整机初始化
/// </summary>
public readonly static ushort W_Initial = 2003;
/// <summary>
/// 消息确认
/// </summary>
public readonly static ushort W_InformationConfirm = 2004;
/// <summary>
/// 上位机有报警提示 0无 1用于触发蜂鸣器响
/// </summary>
public readonly static ushort W_PCErrorTip = 2008;
/// <summary>
/// 上位机有警告提示 0无 1用于触发蜂鸣器响
/// </summary>
public readonly static ushort W_PCAlarmTip = 2009;
/// <summary>
/// 旋转料仓任务队列序号
/// </summary>
public readonly static ushort W_RotatorRack_TaskQueue = 2100;
/// <summary>
/// 旋转料仓任务号
/// </summary>
public readonly static ushort W_RotatorRack_TaskNo = 2101;
/// <summary>
/// 旋转料仓任务撤销
/// </summary>
public readonly static ushort W_RotatorRack_TaskCancel = 2199;
/// <summary>
/// 进出托盘任务队列序号
/// </summary>
public readonly static ushort W_InOutPlate_TaskQueue = 2200;
/// <summary>
/// 进出托盘任务号
/// </summary>
public readonly static ushort W_InOutPlate_TaskNo = 2201;
/// <summary>
/// 进出托盘任务撤销
/// </summary>
public readonly static ushort W_InOutPlate_TaskCancel = 2299;
/// <summary>
/// 龙门移载 任务队列序号
/// </summary>
public readonly static ushort W_GantryTransfer_TaskQueue = 2300;
/// <summary>
/// 龙门移载任务号
/// </summary>
public readonly static ushort W_GantryTransfer_TaskNo = 2301;
/// <summary>
/// 1ml移液枪Z轴初始化结果 1OK 2:NG
/// </summary>
public readonly static ushort W_1mlPipetteZInitResult = 2320;
/// <summary>
/// 1ml移液枪初始化结果 1OK 2:NG
/// </summary>
public readonly static ushort W_1mlPipetteInitResult = 2321;
/// <summary>
/// 请求转盘转动
/// </summary>
public readonly static ushort W_Rotate = 2322;
/// <summary>
/// 针加液管道编号
/// </summary>
public readonly static ushort W_NeedleAddLiquidNo = 2323;
/// <summary>
/// 针加液体积
/// </summary>
public readonly static ushort W_NeedleAddLiquidVolume = 2324;
/// <summary>
/// 龙门移载任务撤销
/// </summary>
public readonly static ushort W_GantryTransfer_TaskCancel = 2399;
/// <summary>
/// 低温磁力搅拌
/// </summary>
public readonly static ushort W_LowTemperatureMagneticStir_TaskQueue = 2400;
/// <summary>
/// 低温磁力搅拌任务号
/// </summary>
public readonly static ushort W_LowTemperatureMagneticStir_TaskNo = 2401;
/// <summary>
/// 低温磁力搅拌任务撤销
/// </summary>
public readonly static ushort W_LowTemperatureMagneticStir_TaskCancel = 2499;
///// <summary>
///// 高温震荡
///// </summary>
//public readonly static ushort W_HighTemperatureShake_TaskQueue = 2400;
///// <summary>
///// 高温震荡搅拌任务号
///// </summary>
//public readonly static ushort W_HighTemperatureShake_TaskNo = 2401;
///// <summary>
///// 高温震荡搅拌任务撤销
///// </summary>
//public readonly static ushort W_HighTemperatureShake_TaskCancel = 2499;
/// <summary>
/// 氮吹 任务队列
/// </summary>
public readonly static ushort W_NitrogenBlowing_TaskQueue = 2500;
/// <summary>
/// 氮吹任务号
/// </summary>
public readonly static ushort W_NitrogenBlowing_TaskNo = 2401;
/// <summary>
/// 氮吹任务撤销
/// </summary>
public readonly static ushort W_NitrogenBlowing_TaskCancel = 2599;
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Enums
{
public enum DeviceType
{
Type1ML = 1,
Type5ML = 2
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.Enums
{
public enum PlateformPos
{
/// <summary>
/// 过渡舱外
/// </summary>
TransferOut = 1,
/// <summary>
/// 过渡舱内
/// </summary>
TransferIn = 2,
/// <summary>
/// 取粉末治具的位置,最终停在这
/// </summary>
GloveBox1 = 3,
/// <summary>
/// 除去粉末治具之外的取放位置
/// </summary>
GloveBox2 = 4,
}
}

View File

@ -0,0 +1,14 @@
namespace MegaRobo.C00225155.Entities.Enums;
public enum ViewNames
{
HomeView = 1,
SettingView, //硬件通讯配置
CommunicationDebugView, //通讯调试
FlowConfigView, //配方流程
AdditivePipetteConfigView,
UserView,
LoggerView,
Pipette1mlWindow = 10,
Pipette5mlWindow = 11,
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities
{
public enum DeviceStatus
{
Disable,
Idel,
CarryOn, //搬运中
Initing,
Runing,
Pause,
Finish,
Abort
}
}

View File

@ -0,0 +1,70 @@
namespace MegaRobo.C00225155.Entities.FromWeb
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
/// <summary>
/// Request
/// </summary>
public partial class ConsumableBox
{
[JsonProperty("consumable")]
public List<Consumable> Consumable { get; set; }
}
public partial class Consumable
{
[JsonProperty("detail")]
public List<Detail> Detail { get; set; }
/// <summary>
/// _40mL原液工装 = 1,_12mL样品工装 = 2,_5mL样品工装 = 3,_2mL外瓶工装 = 4,_滤芯瓶工装 = 5,_16mL粉末瓶工装=6
/// ,_125mL粉末瓶工装=7 ,_50uLTip头工装= 8,_300uLTip头工装=9 ,_1000uLTip头工装=10
/// </summary>
[JsonProperty("fixtureType")]
public int FixtureType { get; set; }
/// <summary>
/// 1~7
/// </summary>
[JsonProperty("posInUpload")]
public int PosInUpload { get; set; }
/// <summary>
/// tip余量
/// </summary>
[JsonProperty("remainCount")]
public int RemainCount { get; set; }
/// <summary>
/// 载具二维码
/// </summary>
[JsonProperty("snCode")]
public string SnCode { get; set; }
}
public partial class Detail
{
/// <summary>
/// 母液或粉末名称
/// </summary>
[JsonProperty("code")]
public string Code { get; set; }
/// <summary>
/// 盒中的位置
/// </summary>
[JsonProperty("pos")]
public string Pos { get; set; }
/// <summary>
/// 余量粉末单位mg 液体单位μl
/// </summary>
[JsonProperty("remainWeight")]
public double RemainWeight { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.FromWeb
{
public class ResponseBase
{
public int code { get; set; }
public string msg { get; set; }
public object data { get; set; }
}
}

View File

@ -0,0 +1,186 @@
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using Apifox;
//
// var request = Request.FromJson(jsonString);
namespace MegaRobo.C00225155.Entities.FromWeb
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
/// <summary>
/// 根据BS那最新的要求改的同步序列的数据结构 260116
/// </summary>
public class SampleSeqRequestEx
{
[JsonProperty("config")]
public ConfigEx Config { get; set; }
/// <summary>
/// 样本序列,管子信息
/// </summary>
[JsonProperty("sampleSeqs")]
public List<SampleSeq> SampleSeqs { get; set; }
public SampleSeqRequestEx FromJson(string json) => JsonConvert.DeserializeObject<SampleSeqRequestEx>(json);
public string ToJson() => JsonConvert.SerializeObject(this);
}
public class ConfigEx
{
[JsonProperty("boxInfo")]
public List<BoxInfoEx> BoxInfo { get; set; }
}
public class BoxInfoEx
{
/// <summary>
/// 载具类型_40mL原液工装 = 1,_12mL样品工装 = 2,_5mL样品工装 = 3,_2mL外瓶工装 = 4,_滤芯瓶工装 = 5,_16mL粉末瓶工装=6
/// ,_125mL粉末瓶工装=7 ,_50uLTip头工装= 8,_300uLTip头工装=9 ,_1000uLTip头工装=10
/// </summary>
[JsonProperty("fixtureType")]
public int FixtureType { get; set; } //BoxTypeEnum
/// <summary>
/// 载具所在的位置1~7
/// </summary>
[JsonProperty("posInUpload")]
public int PosInUpload { get; set; }
/// <summary>
/// 载具二维码
/// </summary>
[JsonProperty("carrierQrCode")]
public string SnCode { get; set; }
}
/// <summary>
/// Request
/// </summary>
public class SampleSeqRequest
{
[JsonProperty("config")]
public Config Config { get; set; }
/// <summary>
/// 样本序列,管子信息
/// </summary>
[JsonProperty("sampleSeqs")]
public List<SampleSeq> SampleSeqs { get; set; }
public SampleSeqRequest FromJson(string json) => JsonConvert.DeserializeObject<SampleSeqRequest>(json);
public string ToJson() => JsonConvert.SerializeObject(this);
}
public class Config
{
[JsonProperty("boxInfo")]
public List<SampleSeqConfig> BoxInfo { get; set; }
}
public class SampleSeqConfig
{
/// <summary>
/// 载具类型_40mL原液工装 = 1,_12mL样品工装 = 2,_5mL样品工装 = 3,_2mL外瓶工装 = 4,_滤芯瓶工装 = 5,_16mL粉末瓶工装=6
/// ,_125mL粉末瓶工装=7 ,_50uLTip头工装= 8,_300uLTip头工装=9 ,_1000uLTip头工装=10
/// </summary>
[JsonProperty("fixtureType")]
public int FixtureType { get; set; } //BoxTypeEnum
/// <summary>
/// 载具所在的位置1~7
/// </summary>
[JsonProperty("posInUpload")]
public int PosInUpload { get; set; }
/// <summary>
/// 剩余tip数目fixtureType=8、9、10时需要传入剩余tip数目
/// </summary>
[JsonProperty("remainCount")]
public int RemainCount { get; set; }
/// <summary>
/// 载具二维码
/// </summary>
[JsonProperty("snCode")]
public string SnCode { get; set; }
}
public class SampleSeq
{
/// <summary>
/// 区域deviceKeyA1
/// </summary>
[JsonProperty("area")]
public string Area { get; set; }
/// <summary>
/// 载具1~7
/// </summary>
[JsonProperty("carrier")]
public string Carrier { get; set; }
/// <summary>
/// 样品名称作为5/12ml原液瓶或者粉末需要传入其名称对于样品瓶需要传入样品名称
/// </summary>
[JsonProperty("code")]
public string Code { get; set; }
/// <summary>
/// 位置 孔号
/// </summary>
[JsonProperty("pos")]
public string Pos { get; set; }
[JsonProperty("sampleConfig")]
public SampleConfig SampleConfig { get; set; }
/// <summary>
/// 序列id对于样品瓶每个都不一样其他类型的无所谓
/// </summary>
[JsonProperty("sampleSeqId")]
public long SampleSeqId { get; set; }
/// <summary>
/// 样本类型_5mL = 1,_12mL = 2,其它 = 0
/// </summary>
[JsonProperty("sampleType")]
public string SampleType { get; set; }
}
public class SampleConfig
{
/// <summary>
/// 样品瓶工单信息对于5/12ml样品瓶必填写入加粉加液信息
/// </summary>
[JsonProperty("materialDoseFunctionFlow")]
public List<SampleFunction> MaterialDoseFunctionFlow { get; set; }
}
public class SampleFunction
{
/// <summary>
/// 添加类型0液1固
/// </summary>
[JsonProperty("functionFlowType")]
public int FunctionFlowType { get; set; }
/// <summary>
/// 添加量液体单位是μl,固体单位是mg
/// </summary>
[JsonProperty("targetVolumn")]
public double TargetVolumn { get; set; }
/// <summary>
/// 添加物名称,添加的液体或者固体名称
/// </summary>
[JsonProperty("userMaterialName")]
public string UserMaterialName { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.FromWeb.Test
{
public class Request
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities.FromWeb.Test
{
public class Response: ResponseBase
{
}
}

View File

@ -0,0 +1,152 @@
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace MegaRobo.C00225155.Entities;
public sealed class HttpListenerEx : IDisposable
{
private Task _listener;
private readonly TcpListenerAdapter _tcpListener;
private CancellationTokenSource _cts;
private bool _disposedValue;
private bool _isListening;
public bool IsListening => _isListening;
public Socket Socket => _tcpListener.Socket;
public IPEndPoint LocalEndpoint { get; }
public event EventHandler<HttpListenerRequestEventArgs> Request;
private HttpListener()
{
_cts = null;
}
public HttpListener(IPAddress address, int port)
: this()
{
LocalEndpoint = new IPEndPoint(address, port);
_tcpListener = new TcpListenerAdapter(LocalEndpoint);
}
public HttpListener(IPEndPoint endpoint)
: this()
{
_tcpListener = new TcpListenerAdapter(endpoint);
}
public void Start()
{
if (_disposedValue)
{
throw new ObjectDisposedException("Object has been disposed.");
}
if (_cts != null)
{
throw new InvalidOperationException("HttpListener is already running.");
}
_cts = new CancellationTokenSource();
_isListening = true;
_listener = Task.Run((Func<Task?>)StartListenerAsync, _cts.Token);
}
private async Task StartListenerAsync()
{
try
{
_tcpListener.Start();
while (_isListening)
{
TcpClientAdapter client = await _tcpListener.AcceptTcpClientAsync().ConfigureAwait(continueOnCapturedContext: false);
HttpListenerRequest request = new HttpListenerRequest(client);
Task.Run(async delegate
{
HttpListenerResponse response = new HttpListenerResponse(request, client);
try
{
await request.ProcessAsync();
response.Initialize();
if (this.Request == null)
{
response.NotFound();
response.Close();
}
else
{
this.Request(this, new HttpListenerRequestEventArgs(request, response));
}
}
catch (Exception)
{
response.CloseSocket();
}
});
}
}
finally
{
_isListening = false;
_cts = null;
}
}
public void Close()
{
if (_cts == null)
{
throw new InvalidOperationException("HttpListener is not running.");
}
this.Request = null;
_cts.Cancel();
_cts = null;
_isListening = false;
_tcpListener.Stop();
try
{
_listener.Wait(TimeSpan.FromMilliseconds(1.0));
}
catch (Exception)
{
}
}
public Task<HttpListenerContext> GetContextAsync()
{
TaskCompletionSource<HttpListenerContext> tcs = new TaskCompletionSource<HttpListenerContext>();
EventHandler<HttpListenerRequestEventArgs> requestHandler = null;
requestHandler = delegate (object sender, HttpListenerRequestEventArgs evArgs)
{
HttpListenerContext result = new HttpListenerContext(evArgs.Request, evArgs.Response);
tcs.SetResult(result);
Request -= requestHandler;
};
Request += requestHandler;
return tcs.Task;
}
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
Close();
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
}
}

Some files were not shown because too many files have changed in this diff Show More