ESP8266 WiFi物联网智能插座—上位机软件实现

1、软件架构

上位机主要作为下位机数据上传服务端以及节点调试的控制端,可以等效认为是专属版本调试工具。针对智能插座协议,对于下位机进行可视化监测和管理。
软件技术架构如下,主要为针对 WindowsPC 端应用程序,采用WPF以及C# 实现功能开发,其中包含MVVM架构。

// 日志库-Log4net
// 通信库-SuperSocket
// WPF组件库-HandyControl
// 插件库-G2Cy.Plugins.NETCore.WPF

2、开发环境

主要在Windows10操作系统中,使用Visual Studio 2022 进行开发,项目源码结构如下:

  • G2CyHome.Models : 包含UI部分通用的一些依赖类,例如工具,协议枚举、命令控制类等。
  • G2CyHome.Wpf : 包含主程序相关窗体和类。
  • G2CyHome.WpfOutlet : 主要包含插座UI组件相关类。

3、程序设计

上位机测试程序主要功能如下,其中主要包括:服务配置、节点数据以及节点控制。

4、程序功能

4.1、服务配置

服务配置,主要在当前同局域网下,启动Socket 服务,对应端口和IP 与同局域网下位机形成通信,基础代码逻辑如下,包括UIViewModel以及服务。
1)UI部分

主要代码如下:

<DockPanel><TextBlock Text="v2023.09.27.0001" DockPanel.Dock="Bottom" HorizontalAlignment="Center" Padding="{DynamicResource DefaultControlPadding}" FontSize="{DynamicResource MainFontSize}" Foreground="{DynamicResource SecondaryTextBrush}"></TextBlock><Border DockPanel.Dock="Top" Height="{DynamicResource HeaderHeight}" Padding="38,0,0,0" Background="Transparent" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource BackgroundBrush}"><StackPanel Orientation="Horizontal"><Path Data="{DynamicResource LogoGeometry}" Fill="{DynamicResource InfoBrush}" Width="30" Height="30"></Path><TextBlock Text="调试终端" FontWeight="Normal" Foreground="{DynamicResource TextIconBrush}" FontSize="{DynamicResource HeadFontSize}" VerticalAlignment="Center" Margin="10,6,0,0"></TextBlock></StackPanel></Border><StackPanel Margin="12,18,18,0"><TextBlock Text="服务配置" LineHeight="33.6" FontSize="{DynamicResource TitleFontSize}" Foreground="{DynamicResource PrimaryTextBrush}"></TextBlock><hc:ComboBox x:Name="cmbx_type" SelectedIndex="{Binding Proto,Mode=OneWay}" hc:TitleElement.Title="协议类型" Padding="{DynamicResource TextboxPadding}"  BorderThickness="1" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BorderBrush}"><ComboBoxItem Content="TCP"></ComboBoxItem><ComboBoxItem Content="UDP"></ComboBoxItem></hc:ComboBox><hc:ComboBox x:Name="cmbx_ip" SelectedIndex="0" hc:TitleElement.Title="IP地址" SelectedValue="{Binding IP,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="0,10,0,0" Padding="{DynamicResource TextboxPadding}" BorderThickness="1" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BorderBrush}"></hc:ComboBox><TextBlock Text="默认选择localhost" FontSize="{DynamicResource MainFontSize}" Margin="0,7,0,0" Foreground="{DynamicResource SecondaryTextBrush}"></TextBlock><UniformGrid Rows="1"><hc:TextBox x:Name="txt_port" hc:TitleElement.Title="监听端口" Text="{Binding Port,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Width="68.8" Margin="0,10,0,0" Padding="{DynamicResource TextboxPadding}" BorderThickness="1" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BorderBrush}"></hc:TextBox><ContentControl x:Name="socket_status" Content="停止" Foreground="{DynamicResource PrimaryTextBrush}"hc:TitleElement.Title="服务状态" hc:TitleElement.TitlePlacement="Top" Width="Auto" Margin="10,10,0,0" HorizontalContentAlignment="Left" Padding="{DynamicResource TextboxPadding}" Template="{StaticResource ContentTopTemplate}"></ContentControl></UniformGrid><TextBlock Text="建议端口1000~65535间" FontSize="{DynamicResource MainFontSize}" Margin="0,7,0,0" Foreground="{DynamicResource SecondaryTextBrush}"></TextBlock><UniformGrid Rows="1" Margin="0,12,0,0"><Button x:Name="btn_start" Click="btn_start_Click" Padding="{DynamicResource ButtonPadding}" hc:BackgroundSwitchElement.MouseHoverBackground="{DynamicResource InfoBrush}"  hc:BackgroundSwitchElement.MouseDownBackground="{DynamicResource InfoBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{DynamicResource ButtonCustom}" hc:BorderElement.CornerRadius="{DynamicResource MainCornerRadius}" Content="开启" Background="{DynamicResource InfoBrush}"></Button><Button x:Name="btn_stop" IsEnabled="False" Click="btn_stop_Click"  Padding="{DynamicResource ButtonPadding}" HorizontalAlignment="Right" VerticalAlignment="Center"   Style="{DynamicResource ButtonCustom}" hc:BorderElement.CornerRadius="4" Content="停止" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Background="{DynamicResource DarkPrimaryBrush}"></Button></UniformGrid></StackPanel>
</DockPanel>

2)ViewModel部分,主要代码在ServerCfgVM中。

/// <summary>
/// 服务配置实体
/// </summary>
public class ServerCfgVM : VMBase
{public ServerCfgVM(){Port = 6886;Proto = 0;SSIds = WiFiUtils.GetWiFiSSID().ToList();SelectedMode = 0;// 默认为运行模式ModeIsEnabled = true;// 默认为启用状态ResetEnabled = true;// 默认为启用状态}private string iP;// 协议格式private int proto;public int Proto{get { return proto; }set { proto = value; RaisePropertyChanged(); }}// IP地址public string IP { get {return iP;} set { iP = value; RaisePropertyChanged(); } }// 端口号private int port;private List<string> sSIds;public int Port{get {return port;}set { port = value; RaisePropertyChanged(); }}/// <summary>/// WIFI列表/// </summary>public List<string> SSIds { get => sSIds; set { sSIds = value; RaisePropertyChanged(); } }private string ssid;/// <summary>/// 选中ssid/// </summary>public string Ssid{get { return ssid; }set { ssid = value;  RaisePropertyChanged(); }}/// <summary>/// 选中模式/// </summary>private int selectedMode;/// <summary>/// 选中模式/// </summary>public int SelectedMode{get { return selectedMode; }set { selectedMode = value;  RaisePropertyChanged(); }}private bool modeIsEnabled;/// <summary>/// 是否启用模式切换/// </summary>public bool ModeIsEnabled{get { return modeIsEnabled; }set { modeIsEnabled = value;RaisePropertyChanged(); }}private bool configEnabled;/// <summary>/// 是否启用配置下发/// </summary>public bool ConfigEnabled{get { return configEnabled; }set { configEnabled = value; RaisePropertyChanged(); }}private bool resetEnabled;/// <summary>/// 是否启用配置重置/// </summary>public bool ResetEnabled{get { return resetEnabled; }set { resetEnabled = value; RaisePropertyChanged(); }}
}

3)服务部分
服务主要为Socket 服务端,配置项用于对服务进行监听和关闭服务管理。

try
{SuperSocketHostBuilder<RequestModel> socketHostBuilder = CreateSocketServerBuilder<RequestModel, RequestModelPipelineFilter>();// socket服务配置socketHostBuilder.ConfigureSuperSocket(config =>{config.ClearIdleSessionInterval = 60;config.DefaultTextEncoding = Encoding.UTF8;config.IdleSessionTimeOut = 150;config.Name = "deviceserver";});server = socketHostBuilder.UsePackageDecoder<MyPackageDecoder>().UsePackageHandler(async (s, p) =>{// 更新页面数据IServiceProvider serviceProvider = Program.DefaultHost.Services;var Vm = serviceProvider.GetRequiredService<DeviceListVM>();try{DeviceVm deviceSession = Vm.AppSessions.FirstOrDefault(x => x.SessionID == s.SessionID);if (deviceSession != null){// 判定设备类型switch (p.NodeType){// 智能插座case DeviceNodeType.Outlet:// 执行插座处理逻辑break;case DeviceNodeType.None:default:break;}}}catch (Exception ex){_logger.LogError(ex.Message, ex);}}).UseSession<AppSession>().UseClearIdleSession().UseHostedService<CustomSocketService>().UseInProcSessionContainer().BuildAsServer();await server.StartAsync();UpdateEnabled(true);
}
catch (Exception ex)
{_logger.LogError(ex.Message, ex);
}

4.2、节点数据

在保证数据服务监听已经启动的情况下,采集来自目标选中节点的传输数据。
1)节点协议解析
数据字节包解析类RequestModelPipelineFilter:

public class RequestModelPipelineFilter : FixedHeaderPipelineFilter<RequestModel>
{/// <summary>/// 是否发现头部/// </summary>private bool _foundHeader;/// <summary>/// 头部长度/// </summary>private readonly int _headerSize;/// <summary>/// 目标数据包总长度/// </summary>private int _totalSize;/// <summary>/// 校验长度/// </summary>private int _verifySize;// 设置对应从接收缓冲区中获取的头部字节长度public RequestModelPipelineFilter(): base(3){_verifySize = 1;_headerSize = 3;}/// <summary>/// 从Header头部获取内容长度/// </summary>/// <param name="buffer">数据字节包</param>/// <returns>内容长度</returns>protected override int GetBodyLengthFromHeader(ref ReadOnlySequence<byte> buffer){var reader = new SequenceReader<byte>(buffer);reader.Advance(buffer.Length - 1);//reader.TryRead(out byte length);byte[] bytes = reader.Sequence.Slice(1, 2).ToArray();//Array.Reverse(bytes);int length = BitConverter.ToInt16(bytes, 0);return length;}/// <summary>/// 过滤执行函数/// </summary>/// <param name="reader">数据字节包</param>/// <returns>数据包实例</returns>public override RequestModel Filter(ref SequenceReader<byte> reader){if (!_foundHeader){if (reader.Length < _headerSize){return null;}ReadOnlySequence<byte> buffer = reader.Sequence.Slice(0, _headerSize);int bodyLengthFromHeader = GetBodyLengthFromHeader(ref buffer);if (bodyLengthFromHeader < 0){throw new ProtocolException("Failed to get body length from the package header.");}if (bodyLengthFromHeader == 0){try{return DecodePackage(ref buffer);}finally{reader.Advance(_headerSize);// 重置是否找到头部_foundHeader = false;}}_foundHeader = true;// 总长度_totalSize = bodyLengthFromHeader;}int totalSize = _totalSize;// 判定当前实际数据包长度是否小于目标数据包总长度if (reader.Length < totalSize){return null;}ReadOnlySequence<byte> buffer2 = reader.Sequence.Slice(0, totalSize);try{return DecodePackage(ref buffer2);}finally{reader.Advance(totalSize);// 重置是否找到头部_foundHeader = false;}}
}

数据接收类RequestModel:

public class RequestModel
{public byte[] Data { get; set; }=new byte[0];/// <summary>/// 设备节点类型/// </summary>public DeviceNodeType NodeType { get; set; }/// <summary>/// 功能码/// </summary>public FeatureType  FeatureType { get; set; }public Msg_Type Msg_Type { get; set; }public byte[] GetBytes(Encoding encoding){return this.ToJson().ToBytes(encoding);}/// <summary>/// 构建包数据/// </summary>/// <param name="buffer">原始字节组</param>public static RequestModel CreatedModel(ReadOnlySequence<byte> buffer){RequestModel requestModel = new RequestModel();// 填充数据var reader = new SequenceReader<byte>(buffer);// 获取产品类型reader.TryRead(out byte devicetype);// 高位(设备类型) byte heigth = (byte)(devicetype & 0xf0);requestModel.NodeType = (DeviceNodeType)Enum.ToObject(typeof(DeviceNodeType), heig// 低位(功能码)byte lower = (byte)(devicetype & 0x0f);requestModel.FeatureType = (FeatureType)Enum.ToObject(typeof(FeatureType), lower);int lentype = (int)buffer.Length-3;//包含校验位-crc校验(2)// 跳过总长度(2)reader.Advance(2);try{//string datastr = reader.ReadString(Encoding.UTF8, lentype);byte[] datas = reader.Sequence.Slice(reader.Position,lentype).ToArray();requestModel.Data = datas;}catch (System.Text.Json.JsonException ex){// 异常处理return new RequestModel();}return requestModel;}public static bool VerifyCRC(ReadOnlySequence<byte> buffer){byte[] nobytes = buffer.Slice(0,buffer.Length-2).ToArray();byte[] flagbytes = buffer.Slice(buffer.Length-2).ToArray();byte[] crcs = CRCUtils.Crc18(nobytes, 0, nobytes.Length);for (int i = 0; i < 2; i++){if (flagbytes[i] != crcs[i]){return false;}}return true;}
}public enum Msg_Type
{ // 节点数据上报Upload,// 控制回发Call
}

2)UI展示部分

设备列表:

主要代码:

<DockPanel><Border BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,1,0"><DockPanel Width="192" Background="{DynamicResource SecondaryBackgroundBrush}"><Border DockPanel.Dock="Top" Padding="0,12,0,12" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1"><TextBlock Text="设备列表" FontSize="{DynamicResource TitleFontSize}" Foreground="{DynamicResource PrimaryTextBrush}"VerticalAlignment="Bottom" HorizontalAlignment="Center"></TextBlock></Border><ListBox x:Name="lbx_devices" ItemsSource="{Binding AppSessions}"  FontSize="{DynamicResource SecondFontSize}" Foreground="{DynamicResource SecondaryTextBrush}"SelectedItem="{Binding SeletectedSession,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"Style="{DynamicResource ListBoxCustom}" SelectionChanged="ListBox_SelectionChanged" Background="Transparent" BorderThickness="0" d:ItemsSource="{d:SampleData ItemCount=5}"><ListBox.ItemContainerStyle><Style TargetType="ListBoxItem"><Setter Property="IsSelected" Value="{Binding IsSeleted,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/><Setter Property="hc:IconElement.Geometry" Value="{Binding DeviceType,Converter={StaticResource StringGeometryConvert}}"/><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ListBoxItem"><hc:SimplePanel Height="60"><Border x:Name="Bg"></Border><Border x:Name="selectline" BorderThickness="0,0,6,0" CornerRadius="0,3,3,0" BorderBrush="{DynamicResource InfoBrush}" Height="{TemplateBinding Height}" HorizontalAlignment="Left"></Border><hc:SimplePanel  Margin="40,0,0,0"><Path x:Name="icon" Data="{Binding Path=(hc:IconElement.Geometry),RelativeSource={RelativeSource Mode=TemplatedParent}}" Width="{DynamicResource LargeFontSize}"Height="{DynamicResource LargeFontSize}"Fill="{DynamicResource InfoBrush}" HorizontalAlignment="Left"></Path><StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="36,0,0,0" VerticalAlignment="Center"><TextBlock x:Name="maintxt" FontSize="{DynamicResource MainFontSize}" Foreground="{DynamicResource PrimaryTextBrush}" Text="{Binding DeviceId, StringFormat=0x\{0:X4\}}"></TextBlock><ContentPresenter x:Name="content"HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Content="{Binding DeviceName}"></ContentPresenter></StackPanel></hc:SimplePanel></hc:SimplePanel><ControlTemplate.Triggers><Trigger Property="IsSelected" Value="True"><Setter TargetName="selectline" Property="Visibility" Value="Visible"/><Setter Property="Background" TargetName="Bg" Value="{DynamicResource BackgroundBrush}"/><Setter Property="Fill" TargetName="icon" Value="{DynamicResource InfoBrush}"/><Setter Property="Opacity" TargetName="content" Value="1"/><Setter Property="Opacity" TargetName="maintxt" Value="1"/><Setter Property="Opacity" TargetName="icon" Value="1"/></Trigger><Trigger Property="IsSelected" Value="False"><Setter TargetName="selectline" Property="Visibility" Value="Collapsed"/><Setter Property="Background" TargetName="Bg" Value="Transparent"/><Setter Property="Opacity" TargetName="content" Value=".5"/><Setter Property="Fill" TargetName="icon" Value="{DynamicResource PrimaryTextBrush}"/><Setter Property="Opacity" TargetName="content" Value=".5"/><Setter Property="Opacity" TargetName="maintxt" Value="0.5"/><Setter Property="Opacity" TargetName="icon" Value=".5"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style></ListBox.ItemContainerStyle></ListBox></DockPanel></Border><Border Padding="0,5.8,0,0" Background="{DynamicResource SecondaryBackgroundBrush}"><ContentControl DataContext="{Binding ElementName=lbx_devices,Path=SelectedItem}" Name="DeviceContent"></ContentControl></Border>
</DockPanel>

内容部分:

主要代码:

<TabControl x:Class="G2CyHome.WpfOutlet.Views.OutletFeature"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:hc="https://handyorg.github.io/handycontrol"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"Style="{DynamicResource TabControlCapsuleSolid}" Background="Transparent" BorderThickness="0,1,0,0" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" DataContextChanged="TabControl_DataContextChanged"SelectionChanged="TabControl_SelectionChanged"><TabItem Height="{DynamicResource TabHeadSize}" hc:TitleElement.Background="{DynamicResource InfoBrush}" Background="Transparent" Padding="8" BorderBrush="{DynamicResource BorderBrush}"><TabItem.Header><DockPanel><Path Data="{DynamicResource FeatureGeometry}" Margin="0,0,8,0" Height="{DynamicResource TabLogoSize}" Width="{DynamicResource TabLogoSize}" Fill="{DynamicResource PrimaryTextBrush}"></Path><TextBlock Text="节点数据" Margin="0,0,8,0" Foreground="{DynamicResource PrimaryTextBrush}" FontSize="{DynamicResource TitleFontSize}" VerticalAlignment="Center"></TextBlock></DockPanel></TabItem.Header><DockPanel><hc:UniformSpacingPanel Margin="33,24,33,0" VerticalSpacing="64" Orientation="Vertical"><StackPanel DataContext="{Binding DeviceData}"><TextBlock Text="基本信息" Foreground="{DynamicResource SecondaryTextBrush}" FontSize="{DynamicResource TitleFontSize}"></TextBlock><hc:Divider Margin="0,7" LineStrokeThickness="1" LineStrokeDashArray="2, 2" Orientation="Horizontal" MaxWidth="2000"/><hc:Row Gutter="24" Margin="0,10"><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="节点ID" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Path=Device_Id, StringFormat=0x\{0:X4\}, Mode=OneWay}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="软件版本" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Software_Version,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="硬件版本" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Hardware_Version,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></hc:Col></hc:Row><hc:Row Gutter="24" Margin="0,10"><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="节点IP" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding ClientIP,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="出厂时间" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Release_Time,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="负载时间" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Run_Time,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></hc:Col></hc:Row></StackPanel><StackPanel DataContext="{Binding DeviceData}"><TextBlock Text="周期时间" Foreground="{DynamicResource SecondaryTextBrush}" FontSize="{DynamicResource TitleFontSize}"></TextBlock><hc:Divider Margin="0,7" LineStrokeThickness="1" LineStrokeDashArray="2, 2" Orientation="Horizontal" MaxWidth="2000"/><hc:UniformSpacingPanel Spacing="42" Orientation="Horizontal"><hc:TextBox hc:TitleElement.Title="数据上传周期(秒)"  Width="250" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Upload_Cycle,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox><hc:TextBox hc:TitleElement.Title="数据采样周期(毫秒)" Width="250"  Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Sample_Cycle,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></hc:UniformSpacingPanel></StackPanel><StackPanel DataContext="{Binding DeviceData}"><TextBlock Text="电参数据" Foreground="{DynamicResource SecondaryTextBrush}" FontSize="{DynamicResource TitleFontSize}"></TextBlock><hc:Divider Margin="0,7" LineStrokeThickness="1" LineStrokeDashArray="2, 2" Orientation="Horizontal" MaxWidth="2000"/><hc:UniformSpacingPanel ItemWidth="160" Orientation="Horizontal" Margin="0,10,0,0"><DockPanel><TextBlock Text="V" VerticalAlignment="Center" DockPanel.Dock="Right" Margin="5,0,15,0"></TextBlock><hc:TextBox hc:TitleElement.Title="电压" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Voltage, Mode=OneWay, StringFormat=\{0:F\}, UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></DockPanel><DockPanel><TextBlock Text="mA" VerticalAlignment="Center" DockPanel.Dock="Right" Margin="5,0,15,0"></TextBlock><hc:TextBox hc:TitleElement.Title="电流" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Current,Mode=OneWay, StringFormat=\{0:F\},UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></DockPanel><DockPanel><TextBlock Text="Kw/h" VerticalAlignment="Center" DockPanel.Dock="Right" Margin="5,0,15,0"></TextBlock><hc:TextBox hc:TitleElement.Title="功率" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Power,Mode=OneWay, StringFormat=\{0:F\},UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></DockPanel><DockPanel><TextBlock Text="Kw" VerticalAlignment="Center" DockPanel.Dock="Right" Margin="5,0,15,0"></TextBlock><hc:TextBox hc:TitleElement.Title="电量" Style="{DynamicResource TitleTextBoxStyle}" Text="{Binding Electricity,Mode=OneWay, StringFormat=\{0:F\},UpdateSourceTrigger=PropertyChanged}"></hc:TextBox></DockPanel></hc:UniformSpacingPanel></StackPanel></hc:UniformSpacingPanel></DockPanel></TabItem><!--数据控制--><!--数据调试-->
</TabControl>

4.3、节点控制

1)协议下发
涉及到的下发协议主要包含:控制继电器开关、节点配置、模式切换、控制状态回发。
主要通过数据包协议类型FeatureType进行区分判定:

/// <summary>
/// 功能类型
/// </summary>
public enum FeatureType:byte
{/// <summary>/// 0x01 设备数据上传功能码/// </summary>[Description("数据上传")]Upload = 0x01,/// <summary>/// 0x02 下发响应功能码/// </summary>[Description("下发响应")]Callback = 0x02,/// <summary>/// 0x05 节点控制功能码/// </summary>[Description("节点控制")]Push = 0x05,/// <summary>/// 0x04 节点配置功能码/// </summary>[Description("节点配置")]Config= 0x04,/// <summary>/// 0x03 模式切换功能码/// </summary>[Description("模式切换")]ModeCfg = 0x03,/// <summary>/// 0x06 升级响应功能码/// </summary>[Description("升级响应")]UpdateAck = 0x06,
}

功能类型通过不同类型接口进行下发指令区分:
例如 控制回发类接口,实现IDeviceCallback

/// <summary>
/// 设备响应回发接口
/// </summary>
public interface IDeviceCallback<T>
{/// <summary>/// 客户端IP/// </summary>[JsonPropertyName("clientip")]public string ClientIP { get; set; }/// <summary>/// 设备标识/// </summary>[JsonPropertyName("device_id")]public int Device_Id { get; set; }/// <summary>/// 软件版本号/// </summary>[JsonPropertyName("software_version")]public string Software_Version { get; set; }/// <summary>/// 硬件版本号/// </summary>[JsonPropertyName("hardware_version")]public string Hardware_Version { get; set; }/// <summary>///  状态码:0 响应成功,1 响应失败/// </summary>public bool State_Code { get; set; }// 字节转对象T BytesToObject(byte[] bytes);
}

设备回发基类:

// 设备回发基类
public class DeviceCallback : VMBase, IDeviceCallback<DeviceCallback> {public virtual DeviceCallback BytesToObject(byte[] bytes){return null;}
}

插座控制回发类实现内容如下:

/// <summary>
/// 智能插座响应类
/// </summary>
public class OutletCallback:DeviceCallback
{public override DeviceCallback BytesToObject(byte[] bytes){SequenceReader<byte> read = new SequenceReader<byte>(new ReadOnlySequence<byte>(bytes));// 设备idbyte[] deviceid = new byte[2];read.TryCopyTo(deviceid);read.Advance(deviceid.Length);Device_Id = BitConverter.ToUInt16(deviceid);// 软件版本byte[] software = new byte[15];read.TryCopyTo(software);read.Advance(software.Length);Software_Version = software.GetStrFromBytes(15);// 硬件版本byte[] hardware = new byte[15];read.TryCopyTo(hardware);Hardware_Version = hardware.GetStrFromBytes(15);read.Advance(hardware.Length);// 状态码read.TryRead(out byte state);// 高位(回发功能码) 高位转低位byte heigth = (byte)((state & 0xf0)>>4);FeatureCallType = (FeatureType)Enum.ToObject(typeof(FeatureType), heigth);// 低位(状态码)byte lower = (byte)(state & 0x0f);State_Code = lower == 0x00;return this;}
}

2)UI展示部分

主要代码:

<TabControl x:Class="G2CyHome.WpfOutlet.Views.OutletFeature"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:hc="https://handyorg.github.io/handycontrol"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"Style="{DynamicResource TabControlCapsuleSolid}" Background="Transparent" BorderThickness="0,1,0,0" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" DataContextChanged="TabControl_DataContextChanged"SelectionChanged="TabControl_SelectionChanged"><!--节点数据--><TabItem Height="{DynamicResource TabHeadSize}" Background="Transparent" hc:TitleElement.Background="{DynamicResource InfoBrush}" Padding="8" BorderBrush="{DynamicResource BorderBrush}"><TabItem.Header><DockPanel><Path Data="{DynamicResource ModeGeometry}" Margin="0,0,8,0" Width="{DynamicResource TabLogoSize}" Height="{DynamicResource TabLogoSize}" Fill="{DynamicResource PrimaryTextBrush}"></Path><TextBlock Text="节点控制" Margin="0,0,8,0" Foreground="{DynamicResource PrimaryTextBrush}" FontSize="{DynamicResource TitleFontSize}" VerticalAlignment="Center"></TextBlock></DockPanel></TabItem.Header><DockPanel><hc:UniformSpacingPanel Margin="33,24,33,0" VerticalSpacing="24" Orientation="Vertical"><StackPanel><DockPanel><ContentControl DockPanel.Dock="Right" hc:TitleElement.Title="" VerticalAlignment="Center"><ContentControl.Template><ControlTemplate TargetType="ContentControl"><StackPanel Orientation="Horizontal"><TextBlock FontSize="{DynamicResource SecondFontSize}" VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=(hc:TitleElement.Title)}" Foreground="{DynamicResource PrimaryTextBrush}" ></TextBlock><Path x:Name="Icon" VerticalAlignment="Center" Data="{DynamicResource ModeGeometry}" Height="{DynamicResource MainFontSize}" Margin="8,0,8,0" Width="{DynamicResource MainFontSize}" Fill="{DynamicResource SuccessBrush}"></Path><TextBlock FontSize="{DynamicResource SecondFontSize}" VerticalAlignment="Center" Text="运行模式" Foreground="{DynamicResource PrimaryTextBrush}" ></TextBlock></StackPanel></ControlTemplate></ContentControl.Template></ContentControl><TextBlock Text="继电器控制" Foreground="{DynamicResource SecondaryTextBrush}" FontSize="{DynamicResource TitleFontSize}" VerticalAlignment="Center"></TextBlock></DockPanel><hc:Divider Margin="0, 7" LineStrokeThickness="1" LineStrokeDashArray="2, 2" Orientation="Horizontal" MaxWidth="2000"/><hc:UniformSpacingPanel HorizontalSpacing="80" Orientation="Horizontal" DataContext="{Binding DeviceData}"><StackPanel Orientation="Horizontal"><ContentControl x:Name="content_status" Content="{Binding Relay_Statestr,UpdateSourceTrigger=PropertyChanged,Mode=OneWay}"  VerticalAlignment="Center" Margin="0,10,0,10" hc:TitleElement.Title="继电器状态"><ContentControl.Template><ControlTemplate TargetType="ContentControl"><StackPanel Orientation="Horizontal"><TextBlock FontSize="{DynamicResource MainFontSize}" VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=(hc:TitleElement.Title)}" Foreground="{DynamicResource PrimaryTextBrush}" ></TextBlock><Path x:Name="Icon" VerticalAlignment="Center" HorizontalAlignment="Center" Data="{DynamicResource SwitchGeometry}" Height="{DynamicResource MainFontSize}" Margin="8,0,8,0" Width="{DynamicResource MainFontSize}" Fill="{DynamicResource BorderBrush}"></Path><TextBlock x:Name="txt_status" FontSize="{DynamicResource MainFontSize}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="关闭" Foreground="{DynamicResource PrimaryTextBrush}" ></TextBlock></StackPanel><ControlTemplate.Triggers><Trigger Property="Content" Value="1"><Setter Property="Fill" TargetName="Icon" Value="{DynamicResource InfoBrush}"/><Setter Property="Opacity" Value="1" TargetName="txt_status"/><Setter Property="Text" Value="开启" TargetName="txt_status"/></Trigger><Trigger Property="Content" Value="0"><Setter Property="Fill" TargetName="Icon" Value="{DynamicResource BorderBrush}"/><Setter Property="Opacity" Value=".5" TargetName="txt_status"/><Setter Property="Text" Value="关闭" TargetName="txt_status"/></Trigger></ControlTemplate.Triggers></ControlTemplate></ContentControl.Template></ContentControl><ToggleButton x:Name="check_switch" BorderThickness="0" Width="40" IsEnabled="{Binding IsSwitch,Mode=OneWay}" IsChecked="{Binding Relay_State,Mode=OneWay,Converter={StaticResource Int2BooleanConverter}}" Cursor="Hand" Height="40" Margin="5" HorizontalAlignment="Center" Style="{StaticResource ToggleButtonFlip}"><hc:StatusSwitchElement.CheckedElement><Border Background="{DynamicResource InfoBrush}"><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="" Foreground="{DynamicResource TextIconBrush}"/></Border></hc:StatusSwitchElement.CheckedElement><Border Background="{DynamicResource BackgroundBrush}"><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="" Foreground="{DynamicResource TextIconBrush}"/></Border></ToggleButton></StackPanel></hc:UniformSpacingPanel></StackPanel><StackPanel><DockPanel DataContext="{Binding DeviceCfg}"><ContentControl DockPanel.Dock="Right"  Content="{Binding Relay_State,UpdateSourceTrigger=PropertyChanged,Mode=OneWay}"  VerticalAlignment="Center" Margin="10,10,0,10" hc:TitleElement.Title=""><ContentControl.Template><ControlTemplate TargetType="ContentControl"><StackPanel Orientation="Horizontal"><TextBlock FontSize="{DynamicResource SecondFontSize}" VerticalAlignment="Center" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=(hc:TitleElement.Title)}" Foreground="{DynamicResource PrimaryTextBrush}" ></TextBlock><Path x:Name="Icon" VerticalAlignment="Center" Data="{DynamicResource ModeGeometry}" Height="{DynamicResource MainFontSize}" Margin="8,0,8,0" Width="{DynamicResource MainFontSize}" Fill="{DynamicResource WarningBrush}"></Path><TextBlock FontSize="{DynamicResource SecondFontSize}" VerticalAlignment="Center" Text="配置模式" Foreground="{DynamicResource PrimaryTextBrush}" ></TextBlock></StackPanel></ControlTemplate></ContentControl.Template></ContentControl><TextBlock Text="配置项下发" Foreground="{DynamicResource SecondaryTextBrush}" FontSize="20" VerticalAlignment="Center"></TextBlock></DockPanel><hc:Divider Margin="0,7" LineStrokeThickness="1" LineStrokeDashArray="2, 2" Orientation="Horizontal" MaxWidth="2000"/><hc:Row Gutter="24" Margin="0,10" DataContext="{Binding ServerCfg}"><hc:Col Span="8"><hc:TextBox Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding IP,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" hc:TitleElement.Title="服务地址" hc:TitleElement.TitlePlacement="Left" Background="{DynamicResource BackgroundBrush}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox VerticalContentAlignment="Center" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Port,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" hc:TitleElement.Title="服务端口" Background="{DynamicResource BackgroundBrush}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox Text="{Binding Ssid}" VerticalContentAlignment="Center" VerticalAlignment="Center" FontSize="{DynamicResource MainFontSize}" hc:TitleElement.VerticalAlignment="Center" Background="{DynamicResource BackgroundBrush}"  hc:TitleElement.Title="服务WIFI" hc:TitleElement.TitlePlacement="Left"></hc:TextBox></hc:Col></hc:Row><hc:Row Gutter="24" Margin="0,10" DataContext="{Binding DeviceCfg}"><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="出厂时间" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Release_Time,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="软件版本" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Software_Version,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="硬件版本" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Hardware_Version,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}"></hc:TextBox></hc:Col></hc:Row><hc:Row Gutter="24" Margin="0,10" DataContext="{Binding DeviceCfg}"><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="设备新Id" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Device_NewId,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}"  ></hc:TextBox></hc:Col><hc:Col Span="8"><!--<hc:TextBox Name="txt_nodetype" hc:TitleElement.Title="设备类型" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding NodeType,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}"  ></hc:TextBox>--></hc:Col><hc:Col Span="8"></hc:Col></hc:Row><hc:Row Gutter="24" Margin="0,10" DataContext="{Binding DeviceCfg}"><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="上传周期(秒)" Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Upload_Cycle,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}"></hc:TextBox></hc:Col><hc:Col Span="8"><hc:TextBox hc:TitleElement.Title="采样周期(毫秒)"  Style="{DynamicResource TitleTextBoxBorderStyle}" Text="{Binding Sample_Cycle,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Background="{DynamicResource BackgroundBrush}" ></hc:TextBox></hc:Col><hc:Col Span="8"></hc:Col></hc:Row><hc:Row Gutter="24" Margin="0,10" DataContext="{Binding ServerCfg}"><hc:Col Span="8"><Button FontSize="{DynamicResource MainFontSize}" Content="配置节点" IsEnabled="{Binding ConfigEnabled,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"  Padding="8" hc:BackgroundSwitchElement.MouseHoverBackground="{DynamicResource InfoBrush}"  hc:BackgroundSwitchElement.MouseDownBackground="{DynamicResource InfoBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{DynamicResource ButtonCustom}" hc:BorderElement.CornerRadius="4" Margin="0,0,0,0"  Background="{DynamicResource InfoBrush}" Click="Button_Click_2"></Button></hc:Col><hc:Col Span="8"><Button FontSize="{DynamicResource MainFontSize}" Content="升级节点"  Padding="8" hc:BackgroundSwitchElement.MouseHoverBackground="{DynamicResource InfoBrush}"  hc:BackgroundSwitchElement.MouseDownBackground="{DynamicResource InfoBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{DynamicResource ButtonCustom}" hc:BorderElement.CornerRadius="4" Margin="0,0,0,0"  Background="{DynamicResource InfoBrush}" Click="Button_Click_1"></Button></hc:Col><hc:Col Span="8"><Button FontSize="{DynamicResource MainFontSize}" Content="重启节点"  Padding="8" hc:BackgroundSwitchElement.MouseHoverBackground="{DynamicResource InfoBrush}"  hc:BackgroundSwitchElement.MouseDownBackground="{DynamicResource InfoBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{DynamicResource ButtonCustom}" hc:BorderElement.CornerRadius="4" Margin="0,0,0,0"  Background="{DynamicResource InfoBrush}" Click="Button_Click_3"></Button></hc:Col><hc:Col Span="8"><Button FontSize="{DynamicResource MainFontSize}" Content="配置重置" IsEnabled="{Binding ResetEnabled,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"  Padding="8" hc:BackgroundSwitchElement.MouseHoverBackground="{DynamicResource InfoBrush}"  hc:BackgroundSwitchElement.MouseDownBackground="{DynamicResource InfoBrush}" HorizontalAlignment="Left" VerticalAlignment="Center" Style="{DynamicResource ButtonCustom}" hc:BorderElement.CornerRadius="4" Margin="0,0,0,0"  Background="{DynamicResource InfoBrush}" Name="btn_reset" Click="btn_reset_Click"></Button></hc:Col></hc:Row></StackPanel></hc:UniformSpacingPanel></DockPanel></TabItem><!--节点调试-->
</TabControl>

5、程序功能特点

程序目前设计采用插件方式加载,数据协议提供数据调试和程序本地日志查看。

本地日志封装如下:

/// <summary>
/// log4net日志记录
/// </summary>
public class Log4NetLogger : ILogger
{private readonly ILog _log;/// <summary>/// 初始化一个<see cref="Log4NetLogger"/>类型的新实例/// </summary>public Log4NetLogger(string loggerRepository, string name){_log = LogManager.GetLogger(loggerRepository, name);}/// <summary>Writes a log entry.</summary>/// <param name="logLevel">日志级别,将按这个级别写不同的日志</param>/// <param name="eventId">事件编号.</param>/// <param name="state">The entry to be written. Can be also an object.</param>/// <param name="exception">The exception related to this entry.</param>/// <param name="formatter">Function to create a <c>string</c> message of the <paramref name="state" /> and <paramref name="exception" />.</param>public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter){if (!IsEnabled(logLevel)){return;}string message = null;if (formatter != null){message = formatter(state, exception);}if (!string.IsNullOrEmpty(message) || exception != null){switch (logLevel){case LogLevel.Trace:case LogLevel.Debug:_log.Debug(message);break;case LogLevel.Information:_log.Info(message);break;case LogLevel.Warning:_log.Warn(message);break;case LogLevel.Error:_log.Error(message, exception);break;case LogLevel.Critical:_log.Fatal(message, exception);break;case LogLevel.None:break;default:_log.Warn($"遇到未知的日志级别 {logLevel}, 使用Info级别写入日志。");_log.Info(message, exception);break;}}}/// <summary>/// Checks if the given <paramref name="logLevel" /> is enabled./// </summary>/// <param name="logLevel">level to be checked.</param>/// <returns><c>true</c> if enabled.</returns>public bool IsEnabled(LogLevel logLevel){switch (logLevel){case LogLevel.Trace:case LogLevel.Debug:return _log.IsDebugEnabled;case LogLevel.Information:return _log.IsInfoEnabled;case LogLevel.Warning:return _log.IsWarnEnabled;case LogLevel.Error:return _log.IsErrorEnabled;case LogLevel.Critical:return _log.IsFatalEnabled;case LogLevel.None:return false;default:throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);}}/// <summary>Begins a logical operation scope.</summary>/// <param name="state">The identifier for the scope.</param>/// <returns>An IDisposable that ends the logical operation scope on dispose.</returns>public IDisposable BeginScope<TState>(TState state){return null;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/293149.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot单元测试全指南:使用Mockito和AssertJ

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

使用hping3网络工具构造TCP/IP数据包和进行DDos攻击

1 概述 hping3是一个强大的命令行工具&#xff0c;用于生成、发送和解析TCP/IP协议的数据包。它是开源的网络安全工具&#xff0c;由Salvatore Sanfilippo开发&#xff0c;主要应用于网络审计、安全测试和故障排查等领域。hping3不仅可以作为普通的网络连通性检测工具&#xf…

基于YOLOV8+Pyqt5光伏太阳能电池板目标检测系统

1、YOLOV8算法 YOLOv8 是当前效果较好的目标检测 算法&#xff0c;它的核心网络来源于 DarkNet-53&#xff0c;该网络初次在 YOLOv3[11] 中被引入&#xff0c;并深受 ResNet[12] 的影响。DarkNet-53 使用了残差机制&#xff0c;并连续添加了卷积模块来加强其功能性。 这 53 层…

VBA高级应用30例应用2:MouseMove鼠标左键按下并移动鼠标事件

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

稀碎从零算法笔记Day35-LeetCode:字典序的第K小数字

要考虑完结《稀碎从零》系列了哈哈哈 这道题和【LC.42 接雨水】&#xff0c;我愿称之为【笔试界的颜良&文丑】 题型&#xff1a;字典树、前缀获取、数组、树的先序遍历 链接&#xff1a;440. 字典序的第K小数字 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1…

(文章复现)考虑分布式电源不确定性的配电网鲁棒动态重构

参考文献&#xff1a; [1]徐俊俊,吴在军,周力,等.考虑分布式电源不确定性的配电网鲁棒动态重构[J].中国电机工程学报,2018,38(16):4715-47254976. 1.摘要 间歇性分布式电源并网使得配电网网络重构过程需要考虑更多的不确定因素。在利用仿射数对分布式电源出力的不确定性进行合…

鸿蒙HarmonyOS应用开发之HID DDK开发指导

场景介绍 HID DDK&#xff08;HID Driver Develop Kit&#xff09;是为开发者提供的HID设备驱动程序开发套件&#xff0c;支持开发者基于用户态&#xff0c;在应用层开发HID设备驱动。提供了一系列主机侧访问设备的接口&#xff0c;包括创建设备、向设备发送事件、销毁设备。 …

负载均衡策略和技术的基本指南

什么是负载均衡器? 负载均衡器将传入的网络流量分布到多个服务器上,以确保没有单个服务器承受过多的负载。通过有效地传播请求,它们提高了应用程序的容量和可靠性。 下面是一些使用负载均衡器的常见场景: 高并发流量:当应用程序面临大量用户请求时,负载均衡器可以将流量分…

【4】单链表(有虚拟头节点)

【4】单链表&#xff08;有虚拟头节点&#xff09; 1、虚拟头节点2、构造方法3、node(int index) 返回索引位置的节点4、添加5、删除6、ArrayList 复杂度分析(1) 复杂度分析(2) 数组的随机访问(3) 动态数组 add(E element) 复杂度分析(4) 动态数组的缩容(5) 复杂度震荡 7、单链…

七、函数的使用方法

函数的调用 nameinput&#xff08;&#xff09;#输入参数并赋值name print&#xff08;name&#xff09;#d打印name 格式&#xff1a;返回值函数名&#xff08;参数&#xff09; def get_sum(n):#形式参数计算累加和:param n::return: sumsum0for i in range(1,n1):sumiprint…

9.Python类与对象

1 面向对象 类和对象都是面向对象中的重要概念。面向对象是一种编程思想&#xff0c; 即按照真实世界的思维方式构建软件系统。 例如&#xff0c;在真实世界的校园里有学生和老师&#xff0c;学生有学号、姓名、所 在班级等属性&#xff08;数据&#xff09;&#xff0c;还有…

【苹果MAC】苹果电脑 LOGI罗技鼠标设置左右切换全屏页面快捷键

首先键盘设置->键盘快捷键 调度中心 设置 f1 f2 为移动一个空间&#xff08;就可以快捷移动了&#xff09; 想要鼠标直接控制&#xff0c;就需要下载官方驱动&#xff0c;来设置按键快捷键&#xff0c;触发 F1 F2 安装 LOGI OPTIONS Logi Options 是一款功能强大且便于使用…

前端虚拟滚动列表 vue虚拟列表

前端虚拟滚动列表 在大型的企业级项目中经常要渲染大量的数据&#xff0c;这种长列表是一个很普遍的场景&#xff0c;当列表内容越来越多就会导致页面滑动卡顿、白屏、数据渲染较慢的问题&#xff1b;大数据量列表性能优化&#xff0c;减少真实dom的渲染 看图&#xff1a;绿色…

设计模式之工厂方法模式精讲

工厂方法模式又叫虚拟构造函数&#xff08;Virtual Constructor&#xff09;模式或者多态性工厂&#xff08;Polymorphic Factory&#xff09;模式。工厂方法模式的用意是定义一个创建产品对象的工厂接口&#xff0c;将实际创建性工作推迟到子类中。 工厂模式可以分为简单工厂…

第六十三回 呼延灼月夜赚关胜 宋公明雪天擒索超-大模型BERT、ERNIE、GPT和GLM的前世今生

神行太保戴宗报信&#xff0c;关胜人马直奔梁上泊&#xff0c;请宋江早早收兵&#xff0c;解梁山之难。宋江派了花荣到飞虎峪左边埋伏&#xff0c;林冲到右边埋伏&#xff0c;再叫呼延灼带着凌振&#xff0c;在离城十里附近布置了火炮&#xff0c;然后才令大军撤退。 李成闻达…

Kubernetes(K8s)技术解析

1. K8s简介 Kubernetes&#xff08;简称K8s&#xff09;是一个开源的容器编排平台&#xff0c;旨在简化容器化应用程序的部署、扩展和管理。为开发者和运维人员提供了丰富的功能和灵活的解决方案&#xff0c;帮助他们更轻松地构建、部署和管理云原生应用程序。以下是关于Kubern…

Oracle 低代码平台 Apex 最新版本 23.2 安装过程

趁春节快结束前&#xff0c;安装了一把APEX &#xff0c;到目前为此&#xff0c;APEX最新版本为23.2&#xff0c;23.2和21版本有一些变化&#xff0c;只是用于验证&#xff0c;我 是使用的单独模式&#xff0c;没有安装TOMAT&#xff0c;下面列一下安装过程&#xff1a; 1.环境…

云服务器8核32G配置报价大全,腾讯云、阿里云和京东云

8核32G云服务器租用优惠价格表&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云8核32G服务器、腾讯云8核32G和京东云8C32G云主机配置报价&#xff0c;腾讯云和京东云是轻量应用服务器&#xff0c;阿里云是云服务器ECS&#xff1a; 阿里云8核32G服务器 阿里云8核32G服务器价格…

阿里云通用算力型u1云服务器配置性能评测及价格参考

阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;ECS通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xf…

Kafka入门到实战-第五弹

Kafka入门到实战 Kafka常见操作官网地址Kafka概述Kafka的基础操作更新计划 Kafka常见操作 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://kafka.apache.org/Kafka概述 Apache Kafka 是一个开源的分布式事件流平台&…