在上一篇博客中我使用智能涡轮流量计采集了一些数据上传到Hyperledger的链码上,此前对链码的一些数据结构进行了修改,但是还存在一些小问题。
https://blog.csdn.net/qq_43824745/article/details/125876812?spm=1001.2014.3001.5501
此前的test链码如下:
/*
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
type Data struct {
Now string `json:"now(L/H)"`
Total string `json:"total(L)"`
}
type QueryResult struct {
Key string `json:"Key"`
Record *Data
}
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
datas := []Data{
Data{Now:"0", Total: "0"},
}
for data := range datas {
dataAsBytes, _ := json.Marshal(data)
err := ctx.GetStub().PutState("2022-07-20 00:00", dataAsBytes)
if err != nil {
return fmt.Errorf("Failed to put to world state. %s", err.Error())
}
}
return nil
}
func (s *SmartContract) AddData(ctx contractapi.TransactionContextInterface, dataNumber string, now string, total string) error {
data := Data{
Now: now,
Total: total,
}
dataAsBytes, _ := json.Marshal(data)
return ctx.GetStub().PutState(dataNumber, dataAsBytes)
}
func (s *SmartContract) QueryData(ctx contractapi.TransactionContextInterface, dataNumber string) (*Data, error) {
dataAsBytes, err := ctx.GetStub().GetState(dataNumber)
if err != nil {
return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
}
if dataAsBytes == nil {
return nil, fmt.Errorf("%s does not exist", dataNumber)
}
data := new(Data)
_ = json.Unmarshal(dataAsBytes, data)
return data, nil
}
func (s *SmartContract) QueryAllDatas(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
startKey := ""
endKey := ""
resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
results := []QueryResult{}
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
data := new(Data)
_ = json.Unmarshal(queryResponse.Value, data)
queryResult := QueryResult{Key: queryResponse.Key, Record: data}
results = append(results, queryResult)
}
return results, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(new(SmartContract))
if err != nil {
fmt.Printf("Error create test chaincode: %s", err.Error())
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting test chaincode: %s", err.Error())
}
}
可以看到我将采集数据的时间精确到分钟的形式作为Data的Key,这样操作的好处是查询所有数据时不会出现Key按字典序排列时产生乱序的现象,结果如下:
每条查询结果均按时间顺序排列,看起来很合理,但是这样处理在查询单条数据时会有不便,因为单条数据查询时需要指定要查询的数据所对应的Key,而上述的Key值是精确到分钟的时间形式,而用户不可能清楚每条记录都是在具体哪一分钟上传的,所以我设想将Key中的时间调整为日期的形式,再对这一天的每一条数据进行编号,为了不产生字典序排列混乱,我准备用定长的数字ID进行编号。
考虑到脚本运行间隔是5分钟,一天能产生的数据总量为不到300条,所以用三位ID来编号是足够的,比如Key为“2022-07-27 010”,其含义就是2022年7月27日这天的第10条数据,这样设计我认为对于用户查询单条数据这一需求算是比较合理的实现吧。由于编号只是对应于当天的编号,到第二天就需要重新从“001”开始计数,所以在脚本为每条数据编写Key值时需要判断一下此条数据是否属于当天,是则ID累加,否则ID归1。
修改后的test.go和shell脚本如下:
test.go:
/*
SPDX-License-Identifier: Apache-2.0
*/
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
type Data struct {
Now string `json:"now(L/H)"`
Total string `json:"total(L)"`
Time string `json:"time"`
}
type QueryResult struct {
Key string `json:"Key"`
Record *Data
}
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
datas := []Data{
Data{Now:"0", Total: "0",Time:"12:00"},
}
for data := range datas {
dataAsBytes, _ := json.Marshal(data)
err := ctx.GetStub().PutState("2022-07-20 000", dataAsBytes)
if err != nil {
return fmt.Errorf("Failed to put to world state. %s", err.Error())
}
}
return nil
}
func (s *SmartContract) AddData(ctx contractapi.TransactionContextInterface, dataNumber string, now string, total string,time string) error {
data := Data{
Now: now,
Total: total,
Time: time,
}
dataAsBytes, _ := json.Marshal(data)
return ctx.GetStub().PutState(dataNumber, dataAsBytes)
}
func (s *SmartContract) QueryData(ctx contractapi.TransactionContextInterface, dataNumber string) (*Data, error) {
dataAsBytes, err := ctx.GetStub().GetState(dataNumber)
if err != nil {
return nil, fmt.Errorf("Failed to read from world state. %s", err.Error())
}
if dataAsBytes == nil {
return nil, fmt.Errorf("%s does not exist", dataNumber)
}
data := new(Data)
_ = json.Unmarshal(dataAsBytes, data)
return data, nil
}
func (s *SmartContract) QueryAllDatas(ctx contractapi.TransactionContextInterface) ([]QueryResult, error) {
startKey := ""
endKey := ""
resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
results := []QueryResult{}
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
data := new(Data)
_ = json.Unmarshal(queryResponse.Value, data)
queryResult := QueryResult{Key: queryResponse.Key, Record: data}
results = append(results, queryResult)
}
return results, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(new(SmartContract))
if err != nil {
fmt.Printf("Error create test chaincode: %s", err.Error())
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting test chaincode: %s", err.Error())
}
}
脚本(没有用真实数据,只用随机数测试了一下):
#!/bin/bash
pre=$(date "+%Y-%m-%d") //pre记录上一条数据的采集时间
num="1" //num记录当前数据是这一天的第几条数据
for i in {1..20}
do
# sudo python /home/pi/RS485_CAN_HAT_Code/485/python/query_now.py
now=$(date "+%Y-%m-%d")
if [ $pre != $now ]
then
num="1"
pre=$now
fi
id=$num
len=${#id}
while [ $len -le 2 ]
do
id="0"$id
let len+=1
done
let num+=1
time=$(date "+%H:%M")
res=$now" "$id
# sleep 3
# echo " " >> data.txt
# while read rows
# do
# n=$rows
# break
# done < data.txt
# sudo python /home/pi/RS485_CAN_HAT_Code/485/python/query_total.py
# sleep 3
# echo " " >> data.txt
# while read rows
# do
# t=$rows
# break
# done < data.txt
n=$RANDOM
t=$RANDOM
echo "这是第"$i"次查询到并添加的数据:"
echo "Now(L/H):"$n" Total(L):"$t" Time:"$time
cmd="'{\"Args\":[\"AddData\",\"$res\",\"$n\",\"$t\",\"$time\"]}'"
echo "Add命令:"$cmd
echo "#!/bin/bash
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n newtest --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c "$cmd "
exit"> add.sh
docker cp add.sh cli:/opt/gopath/src/github.com/hyperledger/fabric/peer/
docker exec -it cli bash add.sh
sleep 60
#break
done
脚本运行结果:
Org1查询所有数据结果:
Org1查询单条数据结果: