当前位置: 首页 > news >正文

长安链使用Golang编写智能合约教程(二)

长安链2.3.0+的go合约虚拟机和2.3.0以下的不兼容,编译的方式也有差异,所以在ide上做了区分。

教程三会写一些,其他比较常用SDK方法的解释和使用方法

教程一:(长安链2.1.+的版本的智能合约)

教程三:(常见GO SDK的解释与使用) 


编写前的注意事项:

1、运行一条带有Doker_GoVM的链

2、建议直接用官方的在线IDE去写合约,因为写完可以直接测,缺点只是调试不方便。

3、如果自己拉环境在本地写合约,编译时注意编译环境,官方有提醒你去Linux下去编译

4、如果你的链是2.3.+,使用编译器前,请先切换到2.3.+,以防不测


1、首先去新建一个合约工程

这里选择空白模板,其他模板好像是一些web3的规范模板

2、打开main.go文件(有一份示例合约)

/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.SPDX-License-Identifier: Apache-2.0
*/package mainimport ("encoding/json""fmt""log""strconv""chainmaker/pb/protogo""chainmaker/sandbox""chainmaker/sdk"
)type FactContract struct {
}// 存证对象
type Fact struct {FileHash string FileName string Time     int 
}// 新建存证对象
func NewFact(fileHash string, fileName string, time int) *Fact {fact := &Fact{FileHash: fileHash,FileName: fileName,Time:     time,}return fact
}func (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))
}func (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))
}func (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.Save()case "findByFileHash":return f.FindByFileHash()default:return sdk.Error("invalid method")}
}func (f *FactContract) Save() protogo.Response {params := sdk.Instance.GetArgs()// 获取参数fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.Atoi(timeStr)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}// 构建结构体fact := NewFact(fileHash, fileName, time)// 序列化factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("marshal fact failed, err: %s", err))}// 发送事件sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)if err != nil {return sdk.Error("fail to save fact bytes")}// 记录日志sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)sdk.Instance.Infof("[save] fileName=" + fact.FileName)// 返回结果return sdk.Success([]byte(fact.FileName + fact.FileHash))}func (f *FactContract) FindByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to call get_state")}// 反序列化var fact Factif err = json.Unmarshal(result, &fact); err != nil {return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))}// 记录日志sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)// 返回结果return sdk.Success(result)
}func main() {err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}
}

3、示例模板解析

21行:这里的FactContract是合约名称,对应的要和125行main函数err := sandbox.Start(new(FactContract))一致

type FactContract struct {
}

25行:Fact结构体就是要存在区块链中的结构体,根据你自己的需要去变更结构体的字段

type Fact struct {
    FileHash string 
    FileName string 
    Time     int 
}

32行:新建存证对象,根据前面25行Fact 结构体的变化而变化

func NewFact(fileHash string, fileName string, time int) *Fact {
    fact := &Fact{
        FileHash: fileHash,
        FileName: fileName,
        Time:     time,
    }
    return fact
}

41-47行:InitContract、UpgradeContract两个方法,不用动,这是实现合约必须要的两个方法,用于合约初始化和合约升级

func (f *FactContract) InitContract() protogo.Response {
    return sdk.Success([]byte("Init contract success"))
}

func (f *FactContract) UpgradeContract() protogo.Response {
    return sdk.Success([]byte("Upgrade contract success"))
}

49行:InvokeContract是调用合约的方法,根据你的合约种有多少方法,依葫芦画瓢在case ....return 继续补充就行

func (f *FactContract) InvokeContract(method string) protogo.Response {
    switch method {
    case "save":
        return f.Save()
    case "findByFileHash":
        return f.FindByFileHash()
    default:
        return sdk.Error("invalid method")
    }
}

 60行: Save(存证方法)

以下大部分依葫芦画瓢就好了,重点关注以下内容:

66-72行:做了time字段的字符校验,确保是数字

84行:发送事件函数EmitEvent,第一个参数是合约事件主题,第二个参数是合约事件参数、(注意合约事件的数据,参数数量不可大于16写了事件、订阅之后可以监听到事件状态

87行:存储数据函数sdk.Instance.PutStateByte,三个参数 key、field 、value   ,(原本我以为弄个key-value的存储参数就行了,为什么官方要弄个field,我也不理解,但是官方有解释,不过用长安链就遵从他的规则吧)  这里就是说key是一个命名空间,相当于一个域,真正的key是一个拼接串,value是存证的内容。

93、94行:记录日志,可记可不记,写了的话,节点的日志记录会存下来

97行:返回要遵从官方规范

func (f *FactContract) Save() protogo.Response {
    params := sdk.Instance.GetArgs()

    // 获取参数
    fileHash := string(params["file_hash"])
    fileName := string(params["file_name"])
    timeStr := string(params["time"])
    time, err := strconv.Atoi(timeStr)
    if err != nil {
        msg := "time is [" + timeStr + "] not int"
        sdk.Instance.Errorf(msg)
        return sdk.Error(msg)
    }

    // 构建结构体
    fact := NewFact(fileHash, fileName, time)

    // 序列化
    factBytes, err := json.Marshal(fact)
    if err != nil {
        return sdk.Error(fmt.Sprintf("marshal fact failed, err: %s", err))
    }
    // 发送事件
    sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})

    // 存储数据
    err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)
    if err != nil {
        return sdk.Error("fail to save fact bytes")
    }

    // 记录日志
    sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)
    sdk.Instance.Infof("[save] fileName=" + fact.FileName)

    // 返回结果
    return sdk.Success([]byte(fact.FileName + fact.FileHash))

}

 取证方法:

以下大部分依葫芦画瓢就好了,重点关注以下内容:

  • 134行:取证的方法:sdk.Instance.GetStateByte ,这里的“fact_bytes”就是这个合约的域,所以这里填写的要和你在存证中填写的域一致才行。
  • 其他按照规范以葫芦画瓢

func (f *FactContract) FindByFileHash() protogo.Response {
    // 获取参数
    fileHash := string(sdk.Instance.GetArgs()["file_hash"])

    // 查询结果
    result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)
    if err != nil {
        return sdk.Error("failed to call get_state")
    }

    // 反序列化
    var fact Fact
    if err = json.Unmarshal(result, &fact); err != nil {
        return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))
    }

    // 记录日志
    sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)
    sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)

    // 返回结果
    return sdk.Success(result)
}

125行:main函数,这个new合约对象的时候保证名称和最开始21行的结构体名称一样就行了,其他的不用变。

func main() {
    err := sandbox.Start(new(FactContract))
    if err != nil {
        log.Fatal(err)
    }
}



4、新增一个查询历史数据的方法

以上就是main.go模板内容的解析,但是一般情况下,我们还有查询历史数据的需求,模板没有提供,所以这里根据模板继续补充一个查询历史记录的方法:

func (f *FactContract) GetHistoryByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}defer iter.Close()var keyModifications []*sdk.KeyModification// 遍历结果for {if !iter.HasNext() {break}keyModification, err := iter.Next()if err != nil {sdk.Instance.Infof("Error iterating: %v", err)}if keyModification == nil {break}keyModifications = append(keyModifications, keyModification)}jsonBytes, err := json.Marshal(keyModifications)if err != nil {return sdk.Error(fmt.Sprintf("Error marshaling keyModifications: %v", err))}// 返回结果return sdk.Success(jsonBytes)
}

方法解析:

这里我只解释重点步骤

1、调用查询历史数据接口

iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)

返回值类型:

后面就是根据返回值的结构进行遍历,

var keyModifications []*sdk.KeyModification

把结果放在 keyModifications  然后进行序列化,返回

 5、新增一个删除方法

注意,这里虽然是一个删除方法,但是不是真的删除,只有有一个isDelete的字段,如果调用了这个方法,某个域对应的hash会被标记为删除。代码不在解释

func (f *FactContract) DeleteByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果err := sdk.Instance.DelState("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}// 返回结果return sdk.Success(nil)
}

6、完整合约

/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.SPDX-License-Identifier: Apache-2.0
*/package mainimport ("chainmaker/pb/protogo""chainmaker/sandbox""chainmaker/sdk""encoding/json""fmt""log""strconv"
)type FactContract struct {
}// 存证对象
type Fact struct {FileHash stringFileName stringTime     int
}// 新建存证对象
func NewFact(fileHash string, fileName string, time int) *Fact {fact := &Fact{FileHash: fileHash,FileName: fileName,Time:     time,}return fact
}func (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))
}func (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))
}func (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.Save()case "findByFileHash":return f.FindByFileHash()case "deltedByFileHash":return f.DeleteByFileHash()case "getHistoryByFileHash":return f.GetHistoryByFileHash()default:return sdk.Error("invalid method")}
}func (f *FactContract) Save() protogo.Response {params := sdk.Instance.GetArgs()// 获取参数fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.Atoi(timeStr)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}// 构建结构体fact := NewFact(fileHash, fileName, time)// 序列化factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("传过来的参数序列化失败, err: %s", err))}// 发送事件sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)if err != nil {return sdk.Error("fail to save fact bytes")}// 记录日志// sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)// sdk.Instance.Infof("[save] fileName=" + fact.FileName)createUser, _ := sdk.Instance.GetSenderRole()sdk.Instance.Infof("[saveUser] create=" + createUser)// 返回结果return sdk.Success([]byte(fact.FileName + fact.FileHash))}func (f *FactContract) FindByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to call get_state")}// 反序列化var fact Factif err = json.Unmarshal(result, &fact); err != nil {return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))}// 记录日志sdk.Instance.Infof("[find_by_file_hash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)// 返回结果return sdk.Success(result)
}func (f *FactContract) DeleteByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果err := sdk.Instance.DelState("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}// 返回结果return sdk.Success(nil)
}func (f *FactContract) GetHistoryByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果iter, err := sdk.Instance.NewHistoryKvIterForKey("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to delere get_state")}defer iter.Close()var keyModifications []*sdk.KeyModification// 遍历结果for {if !iter.HasNext() {break}keyModification, err := iter.Next()if err != nil {sdk.Instance.Infof("Error iterating: %v", err)}if keyModification == nil {break}keyModifications = append(keyModifications, keyModification)sdk.Instance.Infof("Key: %s, Field: %s, Value: %s, TxId: %s, BlockHeight: %d, IsDelete: %t, Timestamp: %s, \n",keyModification.Key, keyModification.Field, keyModification.Value, keyModification.TxId, keyModification.BlockHeight, keyModification.IsDelete, keyModification.Timestamp)}jsonBytes, err := json.Marshal(keyModifications)if err != nil {return sdk.Error(fmt.Sprintf("Error marshaling keyModifications: %v", err))}// 返回结果return sdk.Success(jsonBytes)
}func main() {err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}
}

7、结果展示

1、部署demo2合约

2、发起上链,上链了3条数据,其中file_hash我都是输入的1

3、查询某一个结果

4、删除一个结果

这也是一个上链操作

5、查询历史结果

这里没有格式化,现在拿去专门json格式化一下

[{"Key": "fact_bytes","Field": "1","Value": "eyJGaWxlSGFzaCI6IjEiLCJGaWxlTmFtZSI6IjIiLCJUaW1lIjozfQ==","TxId": "254071cabbca415186ae64956644d2230be111ebb94144d79f168a2252995a88","BlockHeight": 14,"IsDelete": false,"Timestamp": "1716864398"},{"Key": "fact_bytes","Field": "1","Value": "eyJGaWxlSGFzaCI6IjEiLCJGaWxlTmFtZSI6IjIiLCJUaW1lIjozfQ==","TxId": "ce705bbbaedb4315858b4e68b6331f4a947c3eb5a262433dbe2823ad3c87ee06","BlockHeight": 15,"IsDelete": false,"Timestamp": "1716864410"},{"Key": "fact_bytes","Field": "1","Value": "eyJGaWxlSGFzaCI6IjEiLCJGaWxlTmFtZSI6IjIiLCJUaW1lIjozfQ==","TxId": "6e099f7ce81543bf8fd0bf565f741478de53b4bc72834112a8bc0bc5f06f9a47","BlockHeight": 16,"IsDelete": false,"Timestamp": "1716864421"},{"Key": "fact_bytes","Field": "1","Value": "","TxId": "5f76c217063e41ad8c0f2b4ab3fae2418d784c9f0ade416b94715e95214acfc5","BlockHeight": 17,"IsDelete": true,"Timestamp": "1716864504"}
]

value就是存证的字符串,但是这里是base64编码,转码结果如下

(注意:这里你会发现所有的value都是一样的,因为是我存证的数据都是输入的一样的)

转码结果如下:




8、个人理解

  • 官方文档有错:官方文档把key叫做命名空间取了一个固定值,field作为存证hash,这里应该是他们写反了,field 才有域、空间的意思。上面的解析,我还是按照官方错误的来说的。因为他最后存在level_db中的key是拼接的,所以写反写没事,都一样。你可以改成对的。
  • 之所以引入一个命名空间的概念,我猜测应该是为了在数据库中方便查看,因为同一份id可能会被存多次,加了一个空间会方便区分。不过官方也提供了没有命名空间的存储方法PutStateFromKey(key string, value string) error  就是教程一用的方法
  • 删除函数,并不是真的删除,会新上链一条数据,标明某个域、某个key被删除了,但是已经上链的数据不会变动。

相关文章:

长安链使用Golang编写智能合约教程(二)

长安链2.3.0的go合约虚拟机和2.3.0以下的不兼容,编译的方式也有差异,所以在ide上做了区分。 教程三会写一些,其他比较常用SDK方法的解释和使用方法 教程一:(长安链2.1.的版本的智能合约) 教程三&#xff…...

jpom linux发布前端 ruoyi

前置条件 辅助安装 安装jdk curl -fsSL https://jpom.top/docs/install.sh | bash -s Server jdkonly-moduledefault 一键安装maven 后端必备 curl -fsSL https://jpom.top/docs/install.sh | bash -s Server mvnonly-moduledefault 一键安装node 前端必备 curl -fsSL http…...

K8S认证|CKA题库+答案| 15. 备份还原Etcd

目录 15、 备份还原Etcd CKA v1.29.0模拟系统 下载试用 题目: 开始操作: 1)、切换集群 2)、登录master并提权 3)、备份Etcd现有数据 4)、验证备份数据快照 5)、查看节点和Pod状态 6&#xff0…...

软件架构设计属性之一:功能性属性浅析

文章目录 引言一、定义二、关键要素1. 需求满足2. 操作性3. 数据处理4. 业务流程支持5. 合规性 三、 设计原则1. 用户中心设计2. 模块化3. 抽象化4. 可扩展性5. 可维护性6. 设计原则的实施策略7. 设计原则的评估 四、实现策略1. 需求分析2. 功能分解3. 接口设计4. 技术选型5. 迭…...

学习Java的日子 Day48 函数,DOM

Day48 1.流程控制语句 if else for for-in(遍历数组时,跟Java是否一样) While do while break 语句用于跳出循环 continue 用于跳过循环中的一个迭代 2.函数 2.1 JavaScript 函数语法 函数就是包裹在花括号中的代码块,前面使用了关键词 function funct…...

Scikit-Learn朴素贝叶斯

Scikit-Learn朴素贝叶斯 1、朴素贝叶斯1.1、贝叶斯分类1.2、贝叶斯定理1.3、贝叶斯定理的推导1.4、朴素贝叶斯及原理1.5、朴素贝叶斯的优缺点2、Scikit-Learn朴素贝叶斯2.1、Sklearn中的贝叶斯分类器2.2、Scikit-Learn朴素贝叶斯API2.3、Scikit-Learn朴素贝叶斯实践(新闻分类与…...

网页设计步骤总结

第一步&#xff1a;css重置 https://blog.csdn.net/BradenHan/article/details/132122504 第二步&#xff1a;媒体查询不同尺寸加载不同的css文件https://blog.csdn.net/Yi_Lesama/article/details/131184469 <!-- link元素中的CSS媒体查询 --> <link rel"styl…...

第十三期Big Demo Day亮点项目:CCarbon重塑碳交易生态,助力全球绿色发展

第十三期Big Demo Day活动即将于2024年5月28日在香港数码港的CyberArena隆重举行。我们荣幸地宣布&#xff0c;利用区块链技术优化全球碳交易CCarbon项目将亮相&#xff0c;参与精彩的项目路演。本次活动由ZeeprLabs、BiKing Exchange、Gather冠名赞助&#xff0c;Central Rese…...

python基础知识总结(第一节)

一、python简介&#xff1a; Python是一种解释型&#xff0c;面向对象的高级语言。 Pyhton的语法和动态类型&#xff0c;以及解释性语言的本质&#xff0c;使它一跃成为多数平台上写脚本和快速开发应用的编程语言。 python语言百度百科介绍 二、Python基础语法&#xff1a;…...

DOS学习-目录与文件应用操作经典案例-comp

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一.前言 二.使用 三.案例 案例 1: 基本比较 案例 2: 十进制显示差异 案例 3: 字符形式显…...

原神抽卡点名程序教程(直接下载用)

今天我要给大家分享一个在抖音上特别火的视频——原神抽卡点名程序教程。 废话不多说&#xff0c;直接上链接 &#xff1a;--点击下载原神抽卡程序 步骤1&#xff1a; 步骤2&am…...

gpt-4o继续迭代考场安排程序 一键生成考场清单

接上两篇gpt-4o考场安排-CSDN博客&#xff0c;考场分层次安排&#xff0c;最终exe版-CSDN博客 当然你也可以只看这一篇。 今天又添加了以下功能&#xff0c;程序见后。 1、自动分页&#xff0c;每个考场打印一页 2、添加了打印试场单页眉 3、添加了页脚 第X页&#xff0c;…...

php 设置时区

date_default_timezone_set() 设置所有日期/时间函数使用的默认时区 除了在脚本中使用此函数设置默认时区&#xff0c;还可以使用 INI 设置 date.timezone 设置默认时区。 参数 timezoneId 时区标识符&#xff0c;像 UTC、Africa/Lagos、Asia/Hong_Kong 或 Europe/Lisbon。…...

ArcGIS不同图斑设置不同的透明度

对于设置一个图层的整体的透明度&#xff0c;我们在 ArcGIS制作带蒙版的遥感影像地图http://mp.weixin.qq.com/s?__bizMzIzNjM2NTYxMg&mid2247509080&idx1&sn38dccf0a52bb3bb3758f57114ee38b72&chksme8da161bdfad9f0d363da90959a8524dcf2b60d0e8d999f8ebeef0…...

前端 CSS 经典:图片边框

前言&#xff1a;有这么一个业务&#xff0c;需要边框随着图片宽度的变化而变化&#xff0c;比如一些聊天的气泡框等。 实现原理&#xff1a;使用 border-image 属性 效果图&#xff1a; 实现代码&#xff1a; <!DOCTYPE html> <html lang"en"><he…...

异常有什么,异常类有什么

在Java中&#xff0c;异常&#xff08;Exception&#xff09;是一种在程序运行过程中出现的不正常情况。异常机制提供了一种从错误中恢复的途径。异常分为两大类&#xff1a;检查异常&#xff08;Checked Exception&#xff09;和运行时异常&#xff08;Runtime Exception&…...

如何解压忘记了密码的加密zip压缩包?这两个方法收藏好!

加密是一种保护信息不被未经授权访问的重要手段。ZIP压缩包作为一种常见的文件压缩格式&#xff0c;zip文件加密是很多人都回去做的一件事情&#xff0c;那么zip加密文件如何解密&#xff1f;有几种方法可以解密呢&#xff1f;今天介绍几种方法给大家。 一、尝试常用密码 我们…...

java中,怎样用最简单方法实现写word文档

在跨平台环境中实现写word时&#xff0c;如果用现成的库&#xff0c;就会涉及跨平台兼容性问题&#xff0c;比如在安卓与java中实现写word的功能。还有一个问题就是&#xff0c;完全用程序生成word文档&#xff0c;工作量较大。所以采用了模板替换的方法。 docx文档本质就是一…...

大语言模型的工程技巧(三)——分布式计算

相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型&#xff1a;从线性回归到通用人工智能》&#xff0c;欢迎有兴趣的读者多多支持。 本文将讨论如何利用多台机器进行神经网络的分布式训练。利用多台机器来加速大语言模型的训练&#xff0c;是其获得成功的重要原…...

AI开发初体验:昇腾加持,OrangePi AIpro 开发板

文章目录 一、前言二、板子介绍2.1 拆箱2.2 板子规格2.2.1 常规项目2.2.2 扩展项目2.2.3 操作系统 2.3 点板画面 三、AI程序初体验3.1 新奇的地方3.2 运行第一个AI程序3.2.1 硬件连接3.2.2 串口连接3.2.3 开启外部IP端口3.2.4 查询板子IP地址3.2.5 了解 juypter lab 启动脚本&a…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...