|
|
using System;
|
|
|
using System.Buffers.Text;
|
|
|
using System.Collections.Generic;
|
|
|
using System.ComponentModel;
|
|
|
using System.Diagnostics;
|
|
|
using System.Diagnostics.Eventing.Reader;
|
|
|
using System.IO;
|
|
|
using System.IO.Ports;
|
|
|
using System.Linq;
|
|
|
using System.Net.NetworkInformation;
|
|
|
using System.Reflection;
|
|
|
using System.Runtime.InteropServices;
|
|
|
using System.Runtime.Remoting.Messaging;
|
|
|
using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
|
using System.Windows;
|
|
|
using System.Windows.Input;
|
|
|
using System.Windows.Markup;
|
|
|
using System.Windows.Media;
|
|
|
using System.Windows.Media.Imaging;
|
|
|
using System.Windows.Shapes;
|
|
|
using System.Xml;
|
|
|
using Google.Protobuf.WellKnownTypes;
|
|
|
using K4os.Compression.LZ4.Streams.Abstractions;
|
|
|
using Mesnac.Log;
|
|
|
using Microsoft.Xaml.Behaviors.Core;
|
|
|
using Mysqlx.Crud;
|
|
|
using Org.BouncyCastle.Utilities;
|
|
|
using Org.BouncyCastle.Utilities.Encoders;
|
|
|
using TouchSocket.Core;
|
|
|
using TouchSocket.Sockets;
|
|
|
using ZstdSharp.Unsafe;
|
|
|
|
|
|
using static SocketExample.MyTouchClass;
|
|
|
|
|
|
namespace SocketExample
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// MultiClientsWindow.xaml 的交互逻辑
|
|
|
/// </summary>
|
|
|
public partial class TCPWindowV2 : Window
|
|
|
{
|
|
|
public sealed class MySessionClient : TcpSessionClient
|
|
|
{
|
|
|
public Action<ReceivedDataEventArgs> OnMessageReceived { get; set; }
|
|
|
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
|
|
{
|
|
|
//此处逻辑单线程处理。
|
|
|
|
|
|
//此处处理数据,功能相当于Received委托
|
|
|
OnMessageReceived?.Invoke(e);
|
|
|
|
|
|
await base.OnTcpReceived(e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#region 主界面参数
|
|
|
int currentcount = 0;//目前客户端的数量
|
|
|
|
|
|
List<PanelItem> items = new List<PanelItem>();//客户端组件列表
|
|
|
|
|
|
public static double? WindowLeft { get; set; }
|
|
|
public static double? WindowTop { get; set; }
|
|
|
public static double? WindowWidth { get; set; }
|
|
|
public static double? WindowHeight { get; set; }
|
|
|
public static WindowState? OriginWindowState { get; set; }
|
|
|
public static int? Buttonflag { get; set; }
|
|
|
public TCPWindowV2()
|
|
|
{
|
|
|
InitializeComponent();
|
|
|
EnsureFolderExists("日志文件夹");
|
|
|
this.Closing += TCPWindowV2_Closing;
|
|
|
PanelContainer.ItemsSource = items;
|
|
|
SaveWindowSettings();
|
|
|
Buttonflag = 0;
|
|
|
}
|
|
|
#endregion
|
|
|
//结束所有线程,防止心跳线程冗余
|
|
|
private void TCPWindowV2_Closing(object sender, CancelEventArgs e)
|
|
|
{
|
|
|
System.Environment.Exit(0);
|
|
|
}
|
|
|
|
|
|
#region 主界面按钮事件
|
|
|
private void GenerateButton_Click(object sender, RoutedEventArgs e)//添加客户端框体
|
|
|
{
|
|
|
if (int.TryParse(CountTextBox.Text, out int count) && count > 0)
|
|
|
{
|
|
|
//var items = new List<PanelItem>();
|
|
|
for (int i = 0; i < count; i++)
|
|
|
{
|
|
|
items.Add(new PanelItem(i + currentcount));
|
|
|
}
|
|
|
currentcount += count;
|
|
|
PanelContainer.ItemsSource = null;//刷新
|
|
|
PanelContainer.ItemsSource = items;
|
|
|
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
MessageBox.Show("请输入有效的正整数", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
|
}
|
|
|
Welcome_StackPanel.Visibility = Visibility.Visible;
|
|
|
NewTerminal_StackPanel.Visibility = Visibility.Collapsed;
|
|
|
}
|
|
|
|
|
|
private void Shutdown_Button_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
Application.Current.Shutdown();
|
|
|
}
|
|
|
private void CreateTerminal_Button_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
Welcome_StackPanel.Visibility = Visibility.Collapsed;
|
|
|
NewTerminal_StackPanel.Visibility = Visibility.Visible;
|
|
|
}
|
|
|
|
|
|
private void Maxmize_Button_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
if (Buttonflag == 0)
|
|
|
{
|
|
|
this.WindowState = WindowState.Maximized;
|
|
|
MaxMinImage.Source = new BitmapImage(new Uri(@"/取消全屏.png", UriKind.Relative));
|
|
|
Buttonflag = 1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
LoadWindowSettings();
|
|
|
MaxMinImage.Source = new BitmapImage(new Uri(@"/全屏.png", UriKind.Relative));
|
|
|
Buttonflag = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Minimize_Button_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
this.WindowState = WindowState.Minimized;
|
|
|
}
|
|
|
private void ClearButton_Click(object sender, RoutedEventArgs e)//清空客户端
|
|
|
{
|
|
|
foreach (PanelItem item in items)
|
|
|
{
|
|
|
item.dispose();
|
|
|
item.Sessiondispose();
|
|
|
}
|
|
|
items = new List<PanelItem>();
|
|
|
PanelContainer.ItemsSource = null;
|
|
|
currentcount = 0;
|
|
|
}
|
|
|
private void ListenButton_Click(object sender, RoutedEventArgs e)
|
|
|
{
|
|
|
Thread ListeningPortThread = new Thread(ListeningTheard);
|
|
|
ListeningPortThread.IsBackground = true;
|
|
|
ListeningPortThread.Start();
|
|
|
Welcome_StackPanel.Visibility = Visibility.Visible;
|
|
|
NewTerminal_StackPanel.Visibility = Visibility.Collapsed;
|
|
|
}
|
|
|
#endregion
|
|
|
|
|
|
#region 其他功能
|
|
|
|
|
|
private void ListeningTheard() //监听线程
|
|
|
{
|
|
|
IEnumerable<NetworkInterface> networkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
|
|
|
.Where(nic => nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet // 仅以太网
|
|
|
&& nic.OperationalStatus == OperationalStatus.Up); // 仅已启用的接口
|
|
|
CreateListenConnect(networkInterfaces);
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
{
|
|
|
IEnumerable<NetworkInterface> NewnetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces()
|
|
|
.Where(nic => nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet // 仅以太网
|
|
|
&& nic.OperationalStatus == OperationalStatus.Up); // 仅已启用的接口
|
|
|
|
|
|
var UniquenetworkInterfaces = NewnetworkInterfaces
|
|
|
.Where(newNic => !networkInterfaces.Any(oldNic => oldNic.Id == newNic.Id))
|
|
|
.ToList();
|
|
|
|
|
|
if (!UniquenetworkInterfaces.Any())
|
|
|
{
|
|
|
Thread.Sleep(1000);
|
|
|
continue;
|
|
|
}
|
|
|
networkInterfaces = NewnetworkInterfaces;
|
|
|
CreateListenConnect(UniquenetworkInterfaces);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void CreateListenConnect(IEnumerable<NetworkInterface> networkInterfaces) //创建连接
|
|
|
{
|
|
|
foreach (var nic in networkInterfaces)
|
|
|
{
|
|
|
var ipProperties = nic.GetIPProperties();
|
|
|
var unicastAddresses = ipProperties.UnicastAddresses
|
|
|
.Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
|
|
|
|
|
|
foreach (var addr in unicastAddresses)
|
|
|
{
|
|
|
var tcpService = new MyService();
|
|
|
tcpService.SetupAsync(new TouchSocketConfig()//载入配置
|
|
|
.SetListenIPHosts($"tcp://{addr.Address}:20108", 7790)//可以同时监听两个地址
|
|
|
.ConfigureContainer(a =>//容器的配置顺序应该在最前面
|
|
|
{
|
|
|
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
|
|
|
})
|
|
|
.ConfigurePlugins(a =>
|
|
|
{
|
|
|
//a.Add();//此处可以添加插件
|
|
|
}));
|
|
|
tcpService.Connected += ListeningConnected;
|
|
|
tcpService.Closed += ListeningClosed;
|
|
|
tcpService.StartAsync();
|
|
|
Console.WriteLine($"网卡: {nic.Description}, IP: {addr.Address}");
|
|
|
MessageBox.Show($"{addr.Address}监听成功!");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private Task ListeningConnected(MySessionClient client, ConnectedEventArgs e) //连接事件
|
|
|
{
|
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
|
{
|
|
|
items.Add(new PanelItem(client));
|
|
|
PanelContainer.ItemsSource = null;
|
|
|
PanelContainer.ItemsSource = items;
|
|
|
|
|
|
});
|
|
|
return EasyTask.CompletedTask;
|
|
|
}
|
|
|
|
|
|
private Task ListeningClosed(MySessionClient client, ClosedEventArgs e) //断开事件
|
|
|
{
|
|
|
try {
|
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
|
{
|
|
|
foreach (PanelItem item in items)
|
|
|
{
|
|
|
if (item.SessionID.Equals(client.Id))
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
item.Sessiondispose();
|
|
|
items.Remove(item);
|
|
|
PanelContainer.ItemsSource = null;
|
|
|
PanelContainer.ItemsSource = items;
|
|
|
if (items.Count == 0)
|
|
|
break;
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
MessageBox.Show(ex.Message);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
});
|
|
|
}catch(Exception ex)
|
|
|
{
|
|
|
Console.WriteLine(ex.Message);
|
|
|
}
|
|
|
return EasyTask.CompletedTask;
|
|
|
}
|
|
|
|
|
|
public class LinkCommand : ICommand //command方法实现
|
|
|
{
|
|
|
private Action _excute;
|
|
|
|
|
|
private Action<int> _excutewithindex;
|
|
|
|
|
|
public LinkCommand(Action action)
|
|
|
{
|
|
|
_excute = action;
|
|
|
}
|
|
|
|
|
|
public LinkCommand(Action<int> action)
|
|
|
{
|
|
|
_excutewithindex = action;
|
|
|
}
|
|
|
|
|
|
public event EventHandler CanExecuteChanged;
|
|
|
|
|
|
public bool CanExecute(object parameter)
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public void Execute(object parameter)
|
|
|
{
|
|
|
_excute();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static List<TagInfo> GetTagInfos(byte[] AutoDealReportData)
|
|
|
{
|
|
|
List<TagInfo> tagInfoList = new List<TagInfo>();
|
|
|
try
|
|
|
{
|
|
|
int iFirstCountPos = 6; //第一次读取标签次数位置
|
|
|
int iFirstRSSIPos = 7; //第一次读取标签强度位置
|
|
|
int iFirstAnt = 8;
|
|
|
int iFirstPC = 9; //第一次读取标签天线位置
|
|
|
int iFirstLeftBarcketPos = 11;//EPC数据起始位置
|
|
|
UInt16 tempDataCount = 0;
|
|
|
int tempDataRSSI = 0;
|
|
|
UInt16 tempDataANT = 0;
|
|
|
int iBarcodeGroupCount = Convert.ToInt32(AutoDealReportData[5].ToString()); //标签组数
|
|
|
int iBarcodeLength = 16; //标签长度
|
|
|
int iCommonSecondFlag = 0;
|
|
|
int dataLength = Convert.ToInt32(AutoDealReportData[2].ToString());
|
|
|
for (int j = 0; j < iBarcodeGroupCount; j++)
|
|
|
{
|
|
|
TagInfo tag = new TagInfo();
|
|
|
byte[] tempPCByte = new byte[2]; //取出PC
|
|
|
Array.Clear(tempPCByte, 0, 2);
|
|
|
Array.Copy(AutoDealReportData, iFirstPC, tempPCByte, 0, 2);
|
|
|
|
|
|
//PC转二进制取前五位转十进制
|
|
|
int epcLength = Convert.ToInt32(Convert.ToString(Convert.ToInt64(tempPCByte[0].ToString("X"), 16), 2).PadLeft(8, '0').Substring(0, 5), 2) * 2;
|
|
|
|
|
|
iBarcodeLength = epcLength;
|
|
|
|
|
|
byte[] tempDataByte = new byte[epcLength];
|
|
|
Array.Clear(tempDataByte, 0, iBarcodeLength);
|
|
|
Array.Copy(AutoDealReportData, iFirstLeftBarcketPos, tempDataByte, 0, iBarcodeLength);
|
|
|
|
|
|
byte[] tempCountByte = new byte[1]; //取出标签次数
|
|
|
Array.Clear(tempCountByte, 0, 1);
|
|
|
Array.Copy(AutoDealReportData, iFirstCountPos, tempCountByte, 0, 1);
|
|
|
tempDataCount = tempCountByte[0];
|
|
|
|
|
|
byte[] tempRSSIByte = new byte[1]; //取出标签强度
|
|
|
Array.Clear(tempRSSIByte, 0, 1);
|
|
|
Array.Copy(AutoDealReportData, iFirstRSSIPos, tempRSSIByte, 0, 1);
|
|
|
tempDataRSSI = HexStringToNegative(bytesToHexStr(tempRSSIByte, 1));
|
|
|
|
|
|
#region add by wenjy 20220829 取出天线号
|
|
|
byte[] tempAntByte = new byte[1]; //取出天线号
|
|
|
Array.Clear(tempAntByte, 0, 1);
|
|
|
Array.Copy(AutoDealReportData, iFirstAnt, tempAntByte, 0, 1);
|
|
|
tempDataANT = tempAntByte[0];
|
|
|
#endregion
|
|
|
|
|
|
tag.Count = tempDataCount;
|
|
|
tag.RSSI = tempDataRSSI;
|
|
|
tag.EPC = tempDataByte;
|
|
|
tag.EPCstring = System.Text.Encoding.ASCII.GetString(tempDataByte);
|
|
|
tag.PC = tempPCByte;
|
|
|
tag.Antana = tempDataANT;
|
|
|
tagInfoList.Add(tag);
|
|
|
int iBarcodeListLen = tagInfoList.Count; //特别注意,必须这样,要不然会多一条数据
|
|
|
|
|
|
iFirstCountPos = iFirstCountPos + iBarcodeLength + 5; //次数
|
|
|
iFirstRSSIPos = iFirstCountPos + 1; //强度
|
|
|
iFirstAnt = iFirstRSSIPos + 1; //天线
|
|
|
iFirstPC = iFirstAnt + 1;
|
|
|
iFirstLeftBarcketPos = iFirstLeftBarcketPos + iBarcodeLength + 5;
|
|
|
|
|
|
iCommonSecondFlag++;
|
|
|
if (iCommonSecondFlag == iBarcodeGroupCount)
|
|
|
{
|
|
|
//mutauto.ReleaseMutex();
|
|
|
return tagInfoList;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
//LogInfo.Info("----函数调用:Device_AutoDealContent 自动处理函数异常:" + ex.ToString());
|
|
|
//mutauto.ReleaseMutex();
|
|
|
}
|
|
|
return tagInfoList;
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static int HexStringToNegative(string strNumber)
|
|
|
{
|
|
|
|
|
|
int iNegate = 0;
|
|
|
int iNumber = Convert.ToInt32(strNumber, 16);
|
|
|
if (iNumber > 127)
|
|
|
{
|
|
|
int iComplement = iNumber - 1;
|
|
|
string strNegate = string.Empty;
|
|
|
char[] binchar = Convert.ToString(iComplement, 2).PadLeft(8, '0').ToArray();
|
|
|
foreach (char ch in binchar)
|
|
|
{
|
|
|
if (Convert.ToInt32(ch) == 48)
|
|
|
{
|
|
|
strNegate += "1";
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
strNegate += "0";
|
|
|
}
|
|
|
}
|
|
|
iNegate = -Convert.ToInt32(strNegate, 2);
|
|
|
}
|
|
|
return iNegate;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class TagInfo
|
|
|
{
|
|
|
public byte[] PC = new byte[2];
|
|
|
|
|
|
public int Count;
|
|
|
|
|
|
public int RSSI;
|
|
|
|
|
|
public int Antana;
|
|
|
|
|
|
public byte[] EPC;
|
|
|
|
|
|
public byte[] Data;
|
|
|
|
|
|
public string PCstring = null;
|
|
|
|
|
|
public string EPCstring = null;
|
|
|
|
|
|
public DateTime Time;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 将byte数组转换成十六进制字符串 //e.g. { 0x01, 0x01} ---> " 01 01"
|
|
|
/// </summary>
|
|
|
/// <param name="bytes">byte数组</param>
|
|
|
/// <param name="iLen">数组长度</param>
|
|
|
/// <returns>十六进制字符串</returns>
|
|
|
public static string bytesToHexStr(byte[] bytes, int iLen)
|
|
|
{
|
|
|
string returnStr = "";
|
|
|
if (bytes != null)
|
|
|
{
|
|
|
for (int i = 0; i < iLen; i++)
|
|
|
{
|
|
|
returnStr += bytes[i].ToString("X2");
|
|
|
}
|
|
|
}
|
|
|
return returnStr;
|
|
|
}
|
|
|
|
|
|
private static void EnsureFolderExists(string folderPath)
|
|
|
{
|
|
|
// 检查指定的文件夹路径对应的文件夹是否已经存在
|
|
|
if (!Directory.Exists(folderPath))
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
// 如果文件夹不存在,则尝试创建该文件夹
|
|
|
Directory.CreateDirectory(folderPath);
|
|
|
}
|
|
|
catch (IOException ex)
|
|
|
{
|
|
|
// 如果在创建文件夹过程中出现IOException异常(比如权限不足、磁盘已满等原因导致无法创建文件夹)
|
|
|
// 这里通过消息框向用户展示创建文件夹失败的异常信息,方便用户知晓问题所在
|
|
|
MessageBox.Show($"创建文件夹 {folderPath} 失败,异常信息: {ex.Message}");
|
|
|
// 重新抛出异常,让调用这个方法的上层代码知道创建文件夹出现了问题,以便进行进一步的处理,比如终止程序或者尝试其他恢复操作
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private static void EnsureTxtExists(string txtPath, string MachineName, string IPinfo, string Portinfo)
|
|
|
{
|
|
|
// 检查指定的txt路径对应的txt是否已经存在
|
|
|
if (!File.Exists(txtPath))
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
StreamWriter streamWriter = new StreamWriter(txtPath, true);
|
|
|
streamWriter.WriteLineAsync($"设备名称:{MachineName}\nIP地址:{IPinfo}\n端口号:{Portinfo}\n____________________________");
|
|
|
streamWriter.Close();
|
|
|
}
|
|
|
catch (IOException ex)
|
|
|
{
|
|
|
// 如果在创建文件夹过程中出现IOException异常(比如权限不足、磁盘已满等原因导致无法创建文件夹)
|
|
|
// 这里通过消息框向用户展示创建文件夹失败的异常信息,方便用户知晓问题所在
|
|
|
MessageBox.Show($"创建txt {txtPath} 失败,异常信息: {ex.Message}");
|
|
|
// 重新抛出异常,让调用这个方法的上层代码知道创建文件夹出现了问题,以便进行进一步的处理,比如终止程序或者尝试其他恢复操作
|
|
|
throw;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Border_MouseDown(object sender, MouseButtonEventArgs e)
|
|
|
{
|
|
|
if (e.ChangedButton == MouseButton.Left)
|
|
|
{
|
|
|
this.DragMove();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void LoadWindowSettings()
|
|
|
{
|
|
|
if (WindowLeft.HasValue && WindowTop.HasValue)
|
|
|
{
|
|
|
this.Left = WindowLeft.Value;
|
|
|
this.Top = WindowTop.Value;
|
|
|
}
|
|
|
if (WindowWidth.HasValue && WindowHeight.HasValue)
|
|
|
{
|
|
|
this.Width = WindowWidth.Value;
|
|
|
this.Height = WindowHeight.Value;
|
|
|
}
|
|
|
if (OriginWindowState.HasValue)
|
|
|
{
|
|
|
this.WindowState = OriginWindowState.Value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void SaveWindowSettings()
|
|
|
{
|
|
|
WindowLeft = this.Left;
|
|
|
WindowTop = this.Top;
|
|
|
WindowWidth = this.Width;
|
|
|
WindowHeight = this.Height;
|
|
|
OriginWindowState = this.WindowState;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
public class MyService : TcpService<MySessionClient>
|
|
|
{
|
|
|
protected override void LoadConfig(TouchSocketConfig config)
|
|
|
{
|
|
|
//此处加载配置,用户可以从配置中获取配置项。
|
|
|
base.LoadConfig(config);
|
|
|
}
|
|
|
|
|
|
protected override MySessionClient NewClient()
|
|
|
{
|
|
|
return new MySessionClient();
|
|
|
}
|
|
|
|
|
|
protected override async Task OnTcpConnecting(MySessionClient socketClient, ConnectingEventArgs e)
|
|
|
{
|
|
|
//此处逻辑会多线程处理。
|
|
|
|
|
|
//e.Id:对新连接的客户端进行ID初始化,默认情况下是按照设定的规则随机分配的。
|
|
|
//但是按照需求,您可以自定义设置,例如设置为其IP地址。但是需要注意的是id必须在生命周期内唯一。
|
|
|
|
|
|
//e.IsPermitOperation:指示是否允许该客户端链接。
|
|
|
|
|
|
await base.OnTcpConnecting(socketClient, e);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|