C#上位机序列10: 批量读写+点对点更新+数据类型处理

news2024/11/18 3:36:50

一、源码结构

二、运行效果

三、源码解析

PLC批量读写+点对点更新+数据类型处理

优点:根据数据类型,判定监听的地址范围(40120_int 监听两个word:40120 40121;40130_long 监听四个word:40130 40131 40132 40133),添加到UI字典中,PLC批量读取,判定数据变化,查找控件集合,点对点更新,效率高

实现流程:
1. 读取配置文件及创建变量信息(点位名称,地址,数据类型(bool/short/int/float/long/double))

2. 自定义控件绑定参数,用UI字典存储,通过属性get方式,如果是bool类型,直接取Bool字典的点位数据;如果是Word类型,根据数据类型拼装Word字典中的word数据,得到对应数据类型的点位数据;通过set方式,加入到写队列。

复制代码

using PLCBind.CustomControls;
using PLCBind.Service;
using PLCBind.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace PLCBind
{
    public partial class MainForm : Form
    { 
        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            CommonMethods.LoadVar();// 读取配置文件及创建变量信息(点位名称,地址,类型)
            PLCService.Init();      // 读任务&写任务,数据有变化时事件广播通知(自定义控件预先绑定事件)
            BindControlParamModel();// 为按钮绑定参数
        }

        /// <summary>
        /// 为控件绑定参数
        /// </summary>
        private void BindControlParamModel()
        {
            InitControlTag(); 
            SetControlParamModel();
        }

        /// <summary>
        /// 绑定控件变量
        /// </summary>
        void InitControlTag()
        {
            // 左皮带
            lblYX1.Tag = ucBeltLeft1.Tag = "00101";// 启动
            lblZS1.Tag = "40101";// 转速
            lblDY1.Tag = "40120";// 电压
            lblDL1.Tag = "40130";// 电流 
        }

        /// <summary>
        /// 赋值控件参数
        /// </summary>
        void SetControlParamModel()
        {
            foreach (Control item in this.pnlMain.Controls)
            {
                if (item is ITransferUI objItem)
                {
                    var address = item.Tag.ToString();

                    var common = CommonMethods.HomeVariables.Where(obj => obj.PLCAddress == address).FirstOrDefault();

                    if (common != null)
                    {
                        objItem.ParamModel = common;

                        List<string> lstAddress = null;
                        switch (common.DataType)
                        {
                            case DataType.Bool:
                                lstAddress = PLCService.RangeAddress(common.PLCAddress, 0);
                                break;
                            case DataType.Short:
                                lstAddress = PLCService.RangeAddress(common.PLCAddress, 0);
                                break;
                            case DataType.Int:
                            case DataType.Float:
                                lstAddress = PLCService.RangeAddress(common.PLCAddress, 2);// 40120 监听两个word:40120 40121
                                break;
                            case DataType.Long:
                            case DataType.Double:
                                lstAddress = PLCService.RangeAddress(common.PLCAddress, 4);// 40130 监听四个word:40130 40131 40132 40133
                                break;
                        } 

                        foreach (var range in lstAddress)
                        {  
                            CommonMethods.AddControl(CommonMethods.DicHomeControl, range, item);
                        }  
                    }
                }
            }
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            var index = tabControl1.SelectedIndex;

            switch (index)
            {
                case 0:
                    this.ucParameter1.RemoveParams();
                    break;
                case 1:
                    // 参数设置
                    this.pnlSet.Controls.Clear();
                    this.pnlSet.Controls.Add(ucParameter1);
                    this.ucParameter1.ListParams = CommonMethods.SetVariables.Where(s => s.Group == "顺序启动参数").ToList();
                    break;
            }
        } 
    }
}

复制代码

复制代码

using PLCBind.Service;

namespace PLCBind.UIForm
{
    public class BaseParams
    {
        /// <summary>
        /// 描述
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// PLC地址, 多个输入时,用";"分隔开
        /// </summary>
        public string PLCAddress { get; set; } 

        /// <summary>
        /// 数据类型
        /// </summary>
        public DataType DataType { get; set; }

        /// <summary>
        /// 数据分组
        /// </summary>
        public string Group { get; set; }

        /// <summary>
        /// 单位
        /// </summary>
        public string Unit { get; set; }

        /// <summary>
        /// 设置与获取PLC值
        /// </summary>
        public object PLCValue
        {
            get
            {
                object obj = null; 
                switch (DataType)
                {
                    case DataType.Bool:
                        obj = PLCService.GetBool(PLCAddress);
                        break;
                    case DataType.Short:
                        obj = PLCService.GetShort(PLCAddress);
                        break;
                    case DataType.Int:
                        obj = PLCService.GetInt(PLCAddress);
                        break;
                    case DataType.Float:
                        obj = PLCService.GetFloat(PLCAddress);
                        break;
                    case DataType.Long:
                        obj = PLCService.GetLong(PLCAddress);
                        break;
                    case DataType.Double:
                        obj = PLCService.GetDouble(PLCAddress);
                        break;
                } 
                return obj;
            }
            set
            {
                PLCService.AddWriteVariable(PLCAddress, value, DataType);
            }
        } 
    }
}

复制代码

3. 异步任务处理:读任务&写任务,将读到的数据存到Data字典中,判断数据是否有发生变化,如果数据有变化,通过UI字典获取控件集合,调用更新方法

复制代码

using HslCommunication;
using HslCommunication.Core;
using HslCommunication.ModBus;
using PLCBind.CustomControls;
using PLCBind.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using UtilHelper;

namespace PLCBind.Service
{
    public class PLCService
    { 
        public static ConcurrentDictionary<string, bool> DicBoolData = new ConcurrentDictionary<string, bool>();
        public static ConcurrentDictionary<string, Word> DicWordData = new ConcurrentDictionary<string, Word>();
        public static ConcurrentDictionary<string, Word> DicWordChange = new ConcurrentDictionary<string, Word>();
        //
        static ModbusTcpNet client = null;
        static IByteTransform byteTransform;
        static ConcurrentQueue<PLCModel> queueWrite = new ConcurrentQueue<PLCModel>();
         
        // UI通知
        static void NoticeUI(string address, ConcurrentDictionary<string, List<Control>> dicControl)
        {
            dicControl.TryGetValue(address, out List<Control> lstControl);
            if (null != lstControl)
            {
                foreach (var item in lstControl)
                {
                    if (item is ITransferUI objItem)
                    {
                        objItem.NoticeChange();
                    }
                }
            }
        }

        /// <summary>
        /// 事件触发
        /// </summary> 
        public static void DataChange(string address)
        {
            Task.Run(() => NoticeUI(address, CommonMethods.DicHomeControl));
            Task.Run(() => NoticeUI(address, CommonMethods.DicSetControl));
        }

        /// <summary>
        /// 自定义控件内接收到数据变化事件,根据传入address,以及DataType查询监听地址所需要的监听范围(40120_int 监听两个word:40120 40121;40130_long 监听四个word:40130 40131 40132 40133),判断是否属于本控件监听
        /// </summary>
        public static List<string> RangeAddress(string address, int length)
        {
            List<string> lstaddress = new List<string>();

            if (0 == length)
            {
                lstaddress.Add(address);
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    lstaddress.Add(FillAddress((DataHelper.Obj2Int(address) + i).ToString()));
                }
            }
            return lstaddress;
        }

        /// <summary>
        /// 读取时,按位补充0
        /// </summary>
        public static string FillAddress(string val, int length = 5)
        {
            return val.PadLeft(length, '0');
        }

        /// <summary>
        /// 写入时,格式化地址,如:40101 -> 101
        /// </summary> 
        public static string FormatAddress(string val)
        {
            if (val.Length < 5) return val;

            return val.Substring(1, val.Length - 1);
        }

        /// <summary>
        /// 初始化plc通信,开启读写任务
        /// </summary>
        public static void Init()
        {
            client = new ModbusTcpNet(CommonMethods.PLCConfig.HostAddress, CommonMethods.PLCConfig.PortNumber);
            client.AddressStartWithZero = false;
            client.DataFormat = DataFormat.CDAB;
            byteTransform = client.ByteTransform;

            TskPlcRead();
            TskPlcWrite();
        }

        /// <summary>
        /// 获取bool(bool类型)
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public static bool GetBool(string address)
        { 
            try
            {
                bool exist = DicBoolData.TryGetValue(address, out var value);// 字典存储
                if (!exist)
                {
                    Logger.Info($"[Error] PLCService,GetBool,errmsg:查无点位数据({address})");
                }
                return value;
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetBool,errmsg:" + ex.Message);
            }
            return false;
        }

        /// <summary>
        /// 获取word(1个word,2个字节)
        /// </summary> 
        static Word GetAddressWord(string address, int add)
        {
            address = FillAddress((Convert.ToInt32(address) + add).ToString());
            bool exist = DicWordData.TryGetValue(address, out var value);
            if (!exist)
            {
                Logger.Info($"[Error] PLCService,GetAddressWord,errmsg:查无点位数据({address})");
            }
            return value;
        }

        /// <summary>
        /// 拼接字节(多个word)
        /// </summary> 
        static byte[] JoinAddressWord(string address, DataType datatype)
        {
            byte[] ret = null;
            switch (datatype)
            { 
                case DataType.Short:
                    { 
                        var buff = GetAddressWord(address, 0);
                        ret = new byte[2] { buff.Byte1, buff.Byte2 };
                    }
                    break;
                case DataType.Int:
                case DataType.Float:
                    {
                        var buff1 = GetAddressWord(address, 0);
                        var buff2 = GetAddressWord(address, 1);
                        ret = new byte[4] { buff1.Byte1, buff1.Byte2, buff2.Byte1, buff2.Byte2 };
                    } 
                    break;
                case DataType.Long: 
                case DataType.Double:
                    {
                        var buff1 = GetAddressWord(address, 0);
                        var buff2 = GetAddressWord(address, 1);
                        var buff3 = GetAddressWord(address, 2);
                        var buff4 = GetAddressWord(address, 3);
                        ret = new byte[8] { buff1.Byte1, buff1.Byte2, buff2.Byte1, buff2.Byte2, buff3.Byte1, buff3.Byte2, buff4.Byte1, buff4.Byte2 };
                    } 
                    break;
            }
            return ret;
        }

        public static ushort GetShort(string address)
        {
            try
            { 
                var buff = JoinAddressWord(address, DataType.Short);
                return byteTransform.TransUInt16(buff, 0); 
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetShort,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static uint GetInt(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Int);
                return byteTransform.TransUInt32(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetInt,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static float GetFloat(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Float);
                return byteTransform.TransSingle(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetFloat,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static ulong GetLong(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Long);
                return byteTransform.TransUInt64(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetLong,errmsg:" + ex.Message);
            }
            return 0;
        }

        public static double GetDouble(string address)
        {
            try
            {
                var buff = JoinAddressWord(address, DataType.Double);
                return byteTransform.TransDouble(buff, 0);
            }
            catch (Exception ex)
            {
                Logger.Info("[Error] PLCService,GetDouble,errmsg:" + ex.Message);
            }
            return 0;
        }

        /// <summary>
        /// 定时读取
        /// </summary>
        static void TskPlcRead()
        {
            Task.Factory.StartNew(async () =>
            {
                var start_c = CommonMethods.PLCConfig.ReadStart_Coil;
                var start_h = CommonMethods.PLCConfig.ReadStart_Holding;

                bool[] temp_c = null; bool init_c = false;
                byte[] temp_h = null; bool init_h = false;

                while (!CommonMethods.CTS.IsCancellationRequested)
                {
                    try
                    {
                        DicWordChange.Clear();

                        var array_c = (await client.ReadBoolAsync(start_c, (ushort)CommonMethods.PLCConfig.ReadCount_Coil)).Content;
                        var array_h = (await client.ReadAsync(start_h, (ushort)(CommonMethods.PLCConfig.ReadCount_Holding * 2))).Content;// ushort占两个字节

                        if (null != array_c)
                        {
                            // bool类型只占1位,数据有变化直接通知
                            if (null == temp_c)
                            {
                                init_c = true;
                                temp_c = new bool[array_c.Length];
                            }
                            CheckBoolChange("0", start_c, temp_c, array_c, init_c); init_c = false;
                            Array.Copy(array_c, temp_c, array_c.Length);
                        }

                        if (null != array_h)
                        {
                            // word类型数据位(2,4,8),所以要先读取全部的数据,再通知变化
                            if (null == temp_h)
                            {
                                init_h = true;
                                temp_h = new byte[array_h.Length];
                            }
                            CheckWordChange("4", start_h, temp_h, array_h, init_h); init_h = false;
                            Array.Copy(array_h, temp_h, array_h.Length);

                            if (DicWordChange.Count > 0)
                            {
                                foreach (var item in DicWordChange)
                                {
                                    DataChange(item.Key);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Info("[Error] PLCMgr,TskPlcRead,errmsg" + ex.Message);
                    }

                    await Task.Delay(100);
                }
            }, TaskCreationOptions.LongRunning);
        }

        /// <summary>
        /// 检查数据是否有变化(bool类型)
        /// </summary> 
        public static void CheckBoolChange(string flg, string start, bool[] oldbuffer, bool[] newbuffer, bool init)
        {
            for (int i = 0; i < newbuffer.Length; i++)
            {
                // 00101
                string address = flg + FillAddress((i + Convert.ToInt32(start)).ToString(), 4); 

                bool value = newbuffer[i];

                DicBoolData.AddOrUpdate1(address, value); 
               
                if (init || oldbuffer[i] != value)
                { 
                    DataChange(address);
                }
            }
        }

        /// <summary>
        /// 检查数据是否有变化(word类型)
        /// </summary> 
        public static void CheckWordChange(string flg, string start, byte[] oldbuffer, byte[] newbuffer, bool init)
        {
            int index = 0;
            for (int i = 0; i < newbuffer.Length; i = i + 2)
            {
                // 40101
                string address = flg + FillAddress((index + Convert.ToInt32(start)).ToString(), 4); index++; 
               
                byte byte1 = newbuffer[i];
                byte byte2 = newbuffer[i + 1];

                Word buff = new Word() { Byte1 = byte1, Byte2 = byte2 };

                DicWordData.AddOrUpdate1(address, buff);

                if (init || (oldbuffer[i] != byte1 || oldbuffer[i + 1] != byte2))
                {
                    DicWordChange.AddOrUpdate1(address, buff);
                }
            }
        }

        /// <summary>
        /// 添加写入值
        /// </summary> 
        public static void AddWriteVariable(string address, object value, DataType datatype)
        {
            queueWrite.Enqueue(new PLCModel() { Address = address, Value = value, PLCDataType = datatype });//加载值进队列
        } 

        /// <summary>
        /// 定时写入
        /// </summary>
        static void TskPlcWrite()
        { 
            Task.Factory.StartNew(async () =>
            {
                while (!CommonMethods.CTS.IsCancellationRequested)
                {
                    try
                    {
                        if (!queueWrite.IsEmpty)
                        {
                            PLCModel model = null; OperateResult result = null;
                            queueWrite.TryDequeue(out model);

                            var dataype = model.PLCDataType;
                            switch (dataype)
                            {
                                case DataType.Bool:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToBoolean(model.Value));
                                    break;
                                case DataType.Short:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt16(model.Value)); 
                                    break;
                                case DataType.Int:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt32(model.Value));
                                    break;
                                case DataType.Float:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToSingle(model.Value));
                                    break;
                                case DataType.Long:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt64(model.Value));
                                    break;
                                case DataType.Double:
                                    result = await client.WriteAsync(FormatAddress(model.Address), Convert.ToDouble(model.Value));
                                    break;
                            }

                            if (!result.IsSuccess)
                            {
                                Logger.Info("[Error] PLCMgr,TskPlcWrite,errmsg:写入失败," + result.Message);
                            }
                        }
                    }
                    catch (Exception ex)
                    { 
                        Logger.Info("[Error] PLCMgr,TskPlcWrite,errmsg:" + ex.Message);
                    } 

                    await Task.Delay(100);
                }
            }, TaskCreationOptions.LongRunning);
        }
    }
}

复制代码

4. 主界面控件都是静态加载,参数设置的控件是动态加载(点击进入,动态加载变量并监听;离开,移除不监听)

复制代码

using PLCBind.Util;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace PLCBind.UIForm
{
    public partial class ucParameter : UserControl
    {
        public ucParameter()
        {
            InitializeComponent();
        } 

        /// <summary>
        /// 参数集合
        /// </summary>
        public object ListParams
        {
            set
            { 
                RemoveParams();
                 
                if (value is List<BaseParams> Parameters)
                {
                    AddParams(Parameters);
                }
            }
        }

        /// <summary>
        /// 移除参数
        /// </summary>
        public void RemoveParams()
        {
            foreach (Control item in this.tableLayoutPanel1.Controls)
            {
                if (item is ucTextSetting ctrText)
                {
                    CommonMethods.RemoveControl(CommonMethods.DicSetControl, ctrText.Address);// 移除集合
                }
            }

            this.tableLayoutPanel1.Controls.Clear(); // 移除控件
        }

        /// <summary>
        /// 添加参数
        /// </summary> 
        void AddParams(List<BaseParams> objParams)
        { 
            var pamramCount = objParams.Count;
            var pamrammIndex = 0;
            for (int columnIndex = 0; columnIndex < tableLayoutPanel1.ColumnCount; columnIndex++)
            {
                for (int rowIndex = 0; rowIndex < tableLayoutPanel1.RowCount; rowIndex++)
                {
                    if (pamramCount > pamrammIndex)
                    {
                        var common = objParams[pamrammIndex];
                        var address = common.PLCAddress;
                        ucTextSetting ucLbText = new ucTextSetting();
                        ucLbText.Anchor = ((((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left)));
                        ucLbText.ParamModel = common;
                        ucLbText.Address = address;
                        CommonMethods.AddControl(CommonMethods.DicSetControl, address, ucLbText);// 添加集合
                        tableLayoutPanel1.Controls.Add(ucLbText, columnIndex, rowIndex);// 添加控件 
                        pamrammIndex++;
                    }
                }
            }
        }

        private void TableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
        {
            if (e.Row % 2 == 1)
                e.Graphics.FillRectangle(Brushes.White, e.CellBounds);
            else
                e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(192, 224, 248)), e.CellBounds);
        }
    }
}

复制代码

注意事项:
1. 字典类型
Data字典:ConcurrentDictionary<string, bool> DicBoolData;ConcurrentDictionary<string, Word> DicWordData;Word:byte1,byte2
UI字典:ConcurrentDictionary<string, List<Control>> DicHomeControl;ConcurrentDictionary<string, List<Control>> DicSetControl
2. bool类型只占1位,数据有变化直接通知
3. word类型数据位(short:2,int/float:4,long/double:8),所以要先读取全部的数据,再通知变化
4. 自定义控件继承ITransferUI类(属性:ParamModel,方法:NoticeChange),赋值属性ParamModel,其中PLCValue get:通过不同的数据类型,获取字典中的word数据,并拼接合成相应的数据类型;set:传入地址(写入时格式化地址,如:40101->101)及类型

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

运营商光纤资源管理:管理工具的力量

随着通信信息化水平发展&#xff0c;光纤资源已成为现代通信网络的核心要素之一。然而&#xff0c;管理却面临诸多挑战&#xff0c;尤其对于电信运营商而言&#xff0c;面对庞大而复杂的光纤网络资源&#xff0c;怎样做到既不浪费现有资源&#xff0c;又能满足未来业务需求&…

18亿欧元大动作,法国瞄准实现量子飞跃

Quobly 正在开发一种容错量子处理器&#xff08;图片来源&#xff1a;网络&#xff09; 2021年1月&#xff0c;马克龙总统宣布了法国国家量子计算计划&#xff0c;并将为该技术投入高达18亿欧元。 “量子战略至关重要&#xff0c;”马克龙在量子研究中心巴黎萨克雷大学宣布该…

概念解析 | 功率放大器与低噪声放大器:一场关于信号放大的对比

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:功率放大器(PA)与低噪声放大器(LNA)。 功率放大器与低噪声放大器:一场关于信号放大的对比 一、背景介绍 在现代的通信系统中,功率放大器 (Power Amplifier, PA)与低噪声放…

集卡实习总结

规则控制面板 &#xff08;1&#xff09;什么是低代码&#xff1f; 低代码 是借助低代码工具的情况下&#xff0c;开发人员编写少量代码快速开发出企业级应用系统&#xff0c;并帮助企业团队进行数字化转型。低码开发平台借助一整套功能组件&#xff0c;功能分类包括&#xff…

如何查看员工电脑操作记录

作为企业管理者&#xff0c;查看员工电脑操作记录可以帮助了解员工的工作情况和电脑使用情况&#xff0c;以便更好地进行管理。以下是一些查看员工电脑操作记录的方法&#xff1a; 一、电脑监控软件系统 这些软件系统可以实时监控员工电脑的操作情况&#xff0c;包括网页浏览、…

DeOldify 接口化改造 集成 Flask

类似的图片修复项目 GFPGAN 的改造见我另一篇文 https://blog.csdn.net/weixin_43074462/article/details/132497146 DeOldify 是一款开源软件&#xff0c;用于给黑白照片或视频上色&#xff0c;效果还不错。 安装部署教程请参考别的文章&#xff0c;本文基于你给项目跑通&…

极米科技H6 Pro 4K、H6 4K高亮定焦版——开启家用投影4K普及时代

智能投影产业经过几年发展&#xff0c;市场规模正在快速扩大。洛图数据显示&#xff0c;预计今年中国投影出货量有望超700万台&#xff0c;2027年达950万台&#xff0c;可见智能投影产业规模将逐渐壮大&#xff0c;未来可期。2023年&#xff0c;投影行业呈现出全新面貌&#xf…

Linux部署Redis Cluster高可用集群(附带集群节点添加删除以及槽位分配操作详解)

目录 一、前言二、下载安装Redis2.1、选择需要安装的Redis版本2.2、下载并解压Redis2.3、编译安装Redis 三、部署Redis Cluster高可用集群3.1、准备配置文件3.2、启动Redis服务3.3、创建Redis集群3.4、查看集群关系3.5、连接集群Redis进行数据读写以及重定向测试3.6、故障转移和…

数据库设计原则

一、前言 在平时的web系统开发中&#xff0c;数据库是必不可少的一部分&#xff0c;没有了数据库&#xff0c;程序处理的数据就不知如何更好安放。然而数据库没有很好的设计&#xff0c;使用起来也是很不好用的。本节就介绍一下数据库得到基础知识和设计原则。 二、为什么需要…

NEFU离散数学实验3-递推方程

相关概念 递推方程是指一种递归定义&#xff0c;它将问题拆分成更小的子问题&#xff0c;并使用这些子问题的解来计算原问题的解。离散数学中&#xff0c;递推方程通常用于描述数列、组合问题等。 以下是一些递推方程相关的概念和公式&#xff1a; 1. 递推公式&#xff1a;递推…

【嵌入式开发 Linux 常用命令系列 9 -- linux系统终端命令提示符设置(PS1)】

文章目录 Linux PS1 介绍PS1 纯文本和特殊的转义序列PS1 颜色设置 Linux PS1 介绍 在Linux中&#xff0c;PS1&#xff08;Prompt String 1&#xff09;是一个环境变量&#xff0c;用来定义shell命令提示符的显示内容和格式。当你在终端中输入命令时&#xff0c;PS1定义的就是那…

共享WiFi贴推广项目怎么操作?

共享WiFi贴推广项目是一种热门的商业共享服务项目&#xff0c;旨在为用户提供便捷的网络连接&#xff0c;并为代理商带来可观的收益。在本文中&#xff0c;我们将探讨该项目的操作方法&#xff0c;帮助你在推广共享WiFi贴时取得成功。 首先&#xff0c;你需要选择适宜的商户或商…

Ansys Zemax | 解像力仿真设计

附件下载 联系工作人员获取附件 实现 本节介绍了一个模拟解像力图表的例子。 作为一个例子&#xff0c;我们将使用一个等倍率的光学系统&#xff0c;如下图所示&#xff1a; 首先&#xff0c;检查该光学系统的MTF。 分辨率图是用黑白的二进制图像创建的。 MTF设置如下图所…

【JAVA学习笔记】46 - (43)第十一章作业

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter11/src/com/yinhai/homework11 1.枚举类 1.创建一个Color枚举类 2.有RED,BLUE,BL ACK,YELLOW,GREEN这个五个枚举值/对象: 3. Color有三 个属性redValue, greenValue, blueValue, 4.创建构…

uniapp 运行项目在 Android 模拟器中

在开发App时&#xff0c;无论是使用 Flutter 还是 React native&#xff0c;还是使用uni-app 开发跨端App时&#xff0c;总是需要运行调试。一般调试分为两种。 第一&#xff1a;真机调试 第二&#xff1a;模拟器调试 真机调试的好处是可以看到更好的效果&#xff0c;缺点就是…

LibreOffice编辑excel文档如何在单元格中输入手动换行符

用WPS编辑excel文档的时候&#xff0c;要在单元格中输入手动换行符&#xff0c;可以先按住Alt键&#xff0c;然后回车。 而用LibreOffice编辑excel文档&#xff0c;要在单元格中输入手动换行符&#xff0c;可以先按住Ctrl键&#xff0c;然后回车。例如&#xff1a;

业务设计——分库分表下多种登录方式实现【用户名、邮箱、手机号】

业务需求&#xff1a; 实现多种方式的登录流程&#xff0c;要求对用户数据采用分库分表来实现水平扩展 难点分析 难点一 用户登录方式需智能匹配&#xff0c;确保根据其输入的数据类型来确定登录方式&#xff0c;查询数据库指定字段&#xff0c;避免无用查询导致资源浪费 难…

CVPR2023 即插即用 SCConv (附代码)

论文地址&#xff1a;SSCONV 代码地址&#xff1a;https://github.com/cheng-haha/ScConv 1.是什么&#xff1f; SCConv是一种高效的卷积模块&#xff0c;用于压缩卷积神经网络中的冗余特征&#xff0c;以减少计算负荷并提高模型性能。它由空间重构单元(SRU)和信道重构单元(…

如何保障单病种上报的填报效率、质量监控及数据安全

在国家平台对单病种病例进行手工直报&#xff0c;是大多数医院最初获知《关于进一步加强单病种质量管理与控制工作的通知》后的首选方式。随着医院对上报流程与内容的逐步熟练&#xff0c;质控管理的需求开始凸显并占据主要地位&#xff0c;同时为了能更好地适应国家平台的频繁…

基于STM32设计的万能红外遥控器(学习型)

一、项目设计 基于STM32设计的万能红外遥控器(学习型) 随着智能家居和物联网技术的发展,红外遥控器作为传统的智能设备控制方式逐渐被淘汰,但在某些场景下,红外遥控器仍然是一种快速、简单的操作方式,当前介绍了一种基于STM32微控制器设计的红外遥控器,支持接收解码功能和…