diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/README.md b/tools/Mini_Program/qcloud_device_linkage_demo/README.md new file mode 100644 index 00000000..6814ac45 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/README.md @@ -0,0 +1,24 @@ +# 设备联动Demo + +## 配置文件 miniprogram/config.js +``` +// 传感设备配置 +const SensorDevice = { + // 腾讯云物联网通信平台中获取 产品ID和设备名称 + ProductId: "ZLN6Q20QHU", // 产品ID + DeviceName: "dev001", // 设备名称 + // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey + SecretId: "XXXX", + SecretKey: "XXXX", +} + +// 执行设备配置 +const ExecuteDevice = { + ProductId: "U1BZWHF7F9", // 产品ID + DeviceName: "dev_01", // 设备名称 + // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey + SecretId: "XXXX", + SecretKey: "XXXX", + Topic: "U1BZWHF7F9/dev_01/data" +} +``` \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/index.js new file mode 100644 index 00000000..51393eeb --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/index.js @@ -0,0 +1,38 @@ +const tencentcloud = require("tencentcloud-sdk-nodejs"); + +const IotcloudClient = tencentcloud.iotcloud.v20180614.Client; +const models = tencentcloud.iotcloud.v20180614.Models; + +const Credential = tencentcloud.common.Credential; +const ClientProfile = tencentcloud.common.ClientProfile; +const HttpProfile = tencentcloud.common.HttpProfile; + +// 云函数入口函数 +exports.main = async (event, context) => { + let cred = new Credential(event.SecretId, event.SecretKey); + let httpProfile = new HttpProfile(); + httpProfile.endpoint = "iotcloud.tencentcloudapi.com"; + let clientProfile = new ClientProfile(); + clientProfile.httpProfile = httpProfile; + let client = new IotcloudClient(cred, "ap-guangzhou", clientProfile); + let req = new models.PublishMessageRequest(); + // req.Topic = "U1BZWHF7F9/dev_01/data"; + // req.DeviceName = "dev_01"; + // req.ProductId = "U1BZWHF7F9"; + req.Topic = event.Topic + req.DeviceName = event.DeviceName; + req.ProductId = event.ProductId; + req.Payload = event.Payload; + + return new Promise((resolve, reject)=>{ + client.PublishMessage(req, function (errMsg, response) { + if (errMsg) { + console.log(errMsg); + reject(errMsg) + return; + } + console.log(response.to_json_string()); + resolve(response) + }); + }) +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/package.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/package.json new file mode 100644 index 00000000..80f0014c --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-publish/package.json @@ -0,0 +1,14 @@ +{ + "name": "iothub-publish", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "wx-server-sdk": "latest" + } +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/index.js new file mode 100644 index 00000000..1d9921b2 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/index.js @@ -0,0 +1,34 @@ +const tencentcloud = require("tencentcloud-sdk-nodejs"); + +const IotcloudClient = tencentcloud.iotcloud.v20180614.Client; +const models = tencentcloud.iotcloud.v20180614.Models; + +const Credential = tencentcloud.common.Credential; +const ClientProfile = tencentcloud.common.ClientProfile; +const HttpProfile = tencentcloud.common.HttpProfile; + +exports.main = async (event, context) => { + let cred = new Credential(event.SecretId, event.SecretKey); + let httpProfile = new HttpProfile(); + httpProfile.endpoint = "iotcloud.tencentcloudapi.com"; + let clientProfile = new ClientProfile(); + clientProfile.httpProfile = httpProfile; + let client = new IotcloudClient(cred, "ap-guangzhou", clientProfile); + let req = new models.DescribeDeviceShadowRequest(); + req.DeviceName = event.DeviceName; + req.ProductId = event.ProductId; + + return new Promise((resolve, reject)=>{ + client.DescribeDeviceShadow(req, function (errMsg, response) { + + if (errMsg) { + console.log(errMsg); + reject(errMsg) + return; + } + + console.log(response.to_json_string()); + resolve(response) + }); + }) +} diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/package.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/package.json new file mode 100644 index 00000000..f346ef44 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/iothub-shadow-query/package.json @@ -0,0 +1,15 @@ +{ + "name": "iotexplorer", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "tencentcloud-sdk-nodejs": "^3.0.77", + "wx-server-sdk": "latest" + } +} diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/package-lock.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/package-lock.json new file mode 100644 index 00000000..9113190b --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/package-lock.json @@ -0,0 +1,549 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@cloudbase/database": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/@cloudbase/database/download/@cloudbase/database-0.1.1.tgz", + "integrity": "sha1-yf/JK3HhkAVxljkL6odpZ3f/l/8=", + "requires": { + "bson": "^4.0.2" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/@protobufjs/aspromise/download/@protobufjs/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/@protobufjs/base64/download/@protobufjs/base64-1.1.2.tgz", + "integrity": "sha1-TIVzDlm5ofHzSQR9vyQpYDS7JzU=" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/@protobufjs/codegen/download/@protobufjs/codegen-2.0.4.tgz", + "integrity": "sha1-fvN/DQEPsCitGtWXIuUG2SYoFcs=" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/@protobufjs/eventemitter/download/@protobufjs/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/@protobufjs/fetch/download/@protobufjs/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/@protobufjs/float/download/@protobufjs/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/@protobufjs/inquire/download/@protobufjs/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/@protobufjs/path/download/@protobufjs/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/@protobufjs/pool/download/@protobufjs/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/@protobufjs/utf8/download/@protobufjs/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "@types/long": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/@types/long/download/@types/long-4.0.0.tgz", + "integrity": "sha1-cZVR0jUtMBrIuB23Mqy2vcKNve8=" + }, + "@types/node": { + "version": "10.14.13", + "resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-10.14.13.tgz?cache=0&sync_timestamp=1563391049881&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-10.14.13.tgz", + "integrity": "sha1-rHhtYjhgrfOaP1HWKUgKrNam7sc=" + }, + "ajv": { + "version": "6.10.2", + "resolved": "http://mirrors.cloud.tencent.com/npm/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "http://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz", + "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "http://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "http://registry.npm.taobao.org/aws4/download/aws4-1.8.0.tgz", + "integrity": "sha1-8OAD2cqef1nHpQiUXXsu+aBKVC8=" + }, + "base64-js": { + "version": "1.3.0", + "resolved": "http://registry.npm.taobao.org/base64-js/download/base64-js-1.3.0.tgz", + "integrity": "sha1-yrHmEY8FEJXli1KBrqjBzSK/wOM=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "http://mirrors.cloud.tencent.com/npm/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bson": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/bson/download/bson-4.0.2.tgz", + "integrity": "sha1-KSn9/tGhRbHDYZCKK5UKbPS+0sI=", + "requires": { + "buffer": "^5.1.0", + "long": "^4.0.0" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "http://registry.npm.taobao.org/buffer/download/buffer-5.2.1.tgz", + "integrity": "sha1-3Vf6DxCaxZxgJHkETcp7iz0LcdY=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz", + "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "http://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "http://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "http://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "http://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-2.0.1.tgz?cache=0&sync_timestamp=1562517919182&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-deep-equal%2Fdownload%2Ffast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "http://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz?cache=0&sync_timestamp=1562216133657&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fform-data%2Fdownload%2Fform-data-2.3.3.tgz", + "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "http://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "http://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "http://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "http://mirrors.cloud.tencent.com/npm/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "http://registry.npm.taobao.org/has/download/has-1.0.3.tgz", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "requires": { + "function-bind": "^1.1.1" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "http://registry.npm.taobao.org/ieee754/download/ieee754-1.1.13.tgz", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "http://registry.npm.taobao.org/is-regex/download/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "http://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "http://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "http://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "http://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "http://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "http://mirrors.cloud.tencent.com/npm/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npm.taobao.org/lodash.merge/download/lodash.merge-4.6.2.tgz", + "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=" + }, + "long": { + "version": "4.0.0", + "resolved": "http://registry.npm.taobao.org/long/download/long-4.0.0.tgz", + "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.40.0.tgz", + "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI=" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.24.tgz", + "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=", + "requires": { + "mime-db": "1.40.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz", + "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "http://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "protobufjs": { + "version": "6.8.8", + "resolved": "http://mirrors.cloud.tencent.com/npm/protobufjs/-/protobufjs-6.8.8.tgz", + "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.0", + "@types/node": "^10.1.0", + "long": "^4.0.0" + } + }, + "psl": { + "version": "1.2.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/psl/-/psl-1.2.0.tgz", + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "http://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" + }, + "qs": { + "version": "6.5.2", + "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz", + "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" + }, + "request": { + "version": "2.88.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.0.tgz", + "integrity": "sha1-t02uxJsRSPiMZLaNSbHoFcHy9Rk=" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + }, + "sax": { + "version": "1.2.4", + "resolved": "http://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "http://mirrors.cloud.tencent.com/npm/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "tcb-admin-node": { + "version": "1.9.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/tcb-admin-node/-/tcb-admin-node-1.9.0.tgz", + "integrity": "sha512-TYoBo66CEIIw1QzgK4Jq43G45zvBE6ZB35LbDV8wwLQvg6CiZHlmOTVZkgj2YZ8O87ELi+ZE3UBVNZM3nFa6lQ==", + "requires": { + "@cloudbase/database": "0.1.1", + "is-regex": "^1.0.4", + "lodash.merge": "^4.6.1", + "request": "^2.87.0", + "xml2js": "^0.4.19" + } + }, + "tencentcloud-sdk-nodejs": { + "version": "3.0.77", + "resolved": "http://mirrors.cloud.tencent.com/npm/tencentcloud-sdk-nodejs/-/tencentcloud-sdk-nodejs-3.0.77.tgz", + "integrity": "sha512-DULk7IFdR8BQnvC1iRuj+GrYnuU72Lj3oyIO1U2pubf71GtN9PeZ/9km2T7lsCeySU/J6jCTvwVPZaIhip/H1g==", + "requires": { + "request": "^2.85.0" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "http://mirrors.cloud.tencent.com/npm/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "http://mirrors.cloud.tencent.com/npm/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "http://mirrors.cloud.tencent.com/npm/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "http://mirrors.cloud.tencent.com/npm/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "http://mirrors.cloud.tencent.com/npm/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "verror": { + "version": "1.10.0", + "resolved": "http://mirrors.cloud.tencent.com/npm/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "wx-server-sdk": { + "version": "0.8.1", + "resolved": "http://mirrors.cloud.tencent.com/npm/wx-server-sdk/-/wx-server-sdk-0.8.1.tgz", + "integrity": "sha512-mE7O3E7GtRhqk1ukWw2+NiykaO5DaJYiMFCeHrwvekYTVL5Z2nFXnSrUx6nPg/vZ7LWWQbCgQlGOygBjj2+hkQ==", + "requires": { + "protobufjs": "6.8.8", + "tcb-admin-node": "1.9.0", + "tslib": "^1.9.3" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "http://mirrors.cloud.tencent.com/npm/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "http://mirrors.cloud.tencent.com/npm/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } +} diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/index.js new file mode 100644 index 00000000..be1647ca --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/index.js @@ -0,0 +1,37 @@ +// 云函数入口文件 +const cloud = require('wx-server-sdk') +const tencentcloud = require("tencentcloud-sdk-nodejs"); + +const IotexplorerClient = tencentcloud.iotexplorer.v20190423.Client; +const models = tencentcloud.iotexplorer.v20190423.Models; + +const Credential = tencentcloud.common.Credential; +const ClientProfile = tencentcloud.common.ClientProfile; +const HttpProfile = tencentcloud.common.HttpProfile; +cloud.init() + +// 云函数入口函数 +exports.main = async(event, context) => { + console.log("event:", event); + let cred = new Credential(event.SecretId, event.SecretKey); + let httpProfile = new HttpProfile(); + httpProfile.endpoint = "iotexplorer.tencentcloudapi.com"; + let clientProfile = new ClientProfile(); + clientProfile.httpProfile = httpProfile; + let client = new IotexplorerClient(cred, "ap-guangzhou", clientProfile); + + let req = new models.DescribeDeviceDataRequest(); + req.ProductId = event.ProductId; + req.DeviceName = event.DeviceName; + console.log("req:", req); + return new Promise((resolve, reject) => { + client.DescribeDeviceData(req, function(errMsg, response) { + if (errMsg) { + console.log(errMsg); + reject(errMsg); + } + console.log(response); + resolve(response) + }); + }) +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/package.json b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/package.json new file mode 100644 index 00000000..f346ef44 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/cloudfunctions/query/package.json @@ -0,0 +1,15 @@ +{ + "name": "iotexplorer", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "tencentcloud-sdk-nodejs": "^3.0.77", + "wx-server-sdk": "latest" + } +} diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.js new file mode 100644 index 00000000..d1823fd2 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.js @@ -0,0 +1,22 @@ +//app.js +App({ + globalData: { + }, + onLaunch: function () { + wx.setKeepScreenOn({ + keepScreenOn: true + }) + if (!wx.cloud) { + console.error('请使用 2.2.3 或以上的基础库以使用云能力') + } else { + wx.cloud.init({ + // env 参数说明: + // env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源 + // 此处请填入环境 ID, 环境 ID 可打开云控制台查看 + // 如不填则使用默认环境(第一个创建的环境) + env: "tos-demo", + traceUser: true, + }) + } + } +}) diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.json b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.json new file mode 100644 index 00000000..810369f4 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.json @@ -0,0 +1,13 @@ +{ + "pages": [ + "pages/index/index" + ], + "window": { + "backgroundColor": "#F6F6F6", + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#F6F6F6", + "navigationBarTitleText": "设备联动Demo", + "navigationBarTextStyle": "black" + }, + "sitemapLocation": "sitemap.json" +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.wxss b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.wxss new file mode 100644 index 00000000..82678d67 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/app.wxss @@ -0,0 +1,156 @@ +/**app.wxss**/ +.container { + display: flex; + flex-direction: column; + align-items: center; + box-sizing: border-box; +} + +button { + background: initial; +} + +button:focus{ + outline: 0; +} + +button::after{ + border: none; +} + + +page { + background: #f6f6f6; + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.userinfo, .uploader, .tunnel { + margin-top: 40rpx; + height: 140rpx; + width: 100%; + background: #fff; + border: 1px solid rgba(0, 0, 0, 0.1); + border-left: none; + border-right: none; + display: flex; + flex-direction: row; + align-items: center; + transition: all 300ms ease; +} + +.userinfo-avatar { + width: 100rpx; + height: 100rpx; + margin: 20rpx; + border-radius: 50%; + background-size: cover; + background-color: white; +} + +.userinfo-avatar:after { + border: none; +} + +.userinfo-nickname { + font-size: 32rpx; + color: #007aff; + background-color: white; + background-size: cover; +} + +.userinfo-nickname::after { + border: none; +} + +.uploader, .tunnel { + height: auto; + padding: 0 0 0 40rpx; + flex-direction: column; + align-items: flex-start; + box-sizing: border-box; +} + +.uploader-text, .tunnel-text { + width: 100%; + line-height: 52px; + font-size: 34rpx; + color: #007aff; +} + +.uploader-container { + width: 100%; + height: 400rpx; + padding: 20rpx 20rpx 20rpx 0; + display: flex; + align-content: center; + justify-content: center; + box-sizing: border-box; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +.uploader-image { + width: 100%; + height: 360rpx; +} + +.tunnel { + padding: 0 0 0 40rpx; +} + +.tunnel-text { + position: relative; + color: #222; + display: flex; + flex-direction: row; + align-content: center; + justify-content: space-between; + box-sizing: border-box; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +.tunnel-text:first-child { + border-top: none; +} + +.tunnel-switch { + position: absolute; + right: 20rpx; + top: -2rpx; +} + +.disable { + color: #888; +} + +.service { + position: fixed; + right: 40rpx; + bottom: 40rpx; + width: 140rpx; + height: 140rpx; + border-radius: 50%; + background: linear-gradient(#007aff, #0063ce); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); + display: flex; + align-content: center; + justify-content: center; + transition: all 300ms ease; +} + +.service-button { + position: absolute; + top: 40rpx; +} + +.service:active { + box-shadow: none; +} + +.request-text { + padding: 20rpx 0; + font-size: 24rpx; + line-height: 36rpx; + word-break: break-all; +} diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/config.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/config.js new file mode 100644 index 00000000..c3a7f0a8 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/config.js @@ -0,0 +1,24 @@ +// 传感设备配置 +const SensorDevice = { + // 腾讯云物联网通信平台中获取 产品ID和设备名称 + ProductId: "ZLN6Q20QHU", // 产品ID + DeviceName: "dev001", // 设备名称 + // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey + SecretId: "XXXX", + SecretKey: "XXXX", +} + +// 执行设备配置 +const ExecuteDevice = { + ProductId: "U1BZWHF7F9", // 产品ID + DeviceName: "dev_01", // 设备名称 + // 腾讯云控制台-访问管理-访问密钥-API密钥管理中获取 SecretId, SecretKey + SecretId: "XXXX", + SecretKey: "XXXX", + Topic: "U1BZWHF7F9/dev_01/data" +} + +module.exports = { + SensorDevice: SensorDevice, + ExecuteDevice: ExecuteDevice, +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/images/TencentOS_tiny_log.png b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/images/TencentOS_tiny_log.png new file mode 100644 index 00000000..54f21405 Binary files /dev/null and b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/images/TencentOS_tiny_log.png differ diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.js new file mode 100644 index 00000000..62eb148f --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.js @@ -0,0 +1,266 @@ +const app = getApp() +const config = require('../../config.js') +var util = require('../../utils/util.js'); +var wxCharts = require('../../utils/wxcharts.js'); +var lineChart = null; +var startPos = null; + +Page({ + data: { + tempHigh: 20, + sensorDevice: {}, + executeDevice: {}, + chartDataTemp: [], + chartDataHumi: [], + chartCategories: [], + }, + onLoad: async function(options) { + console.log("index onLoad") + while (true) { + let executor = await this.queryExecuteDeviceStatus() + let sensor = await this.querySensorDeviceStatus() + this.renderChart(sensor.temperature.Value, sensor.humidity.Value) + let temp = sensor.temperature.Value + console.log(temp) + if (temp >= this.data.tempHigh) { + if (executor.light == 0) { + wx.showModal({ + title: '提示', + content: `温度达到门限(${this.data.tempHigh}),打开风扇和灯`, + showCancel: false, + }) + this.turnOnLight() + this.turnOnFan() + } + } else { + // this.turnOffLight() + // this.turnOffFan() + } + await util.delayMs(5000) + } + }, + sliderChange(e) { + console.log(e.detail) + this.setData({ + tempHigh: e.detail.value + }) + }, + renderChart(temperature, humidity) { + let epoch = Math.round(new Date().getTime() / 1000) + let timestamp = util.formatEpoch(epoch) + if (this.data.chartDataTemp.length > 5) { + this.data.chartDataTemp.pop(); + this.data.chartDataHumi.pop(); + this.data.chartCategories.pop(); + } + this.data.chartDataTemp.unshift(temperature) + this.data.chartDataHumi.unshift(humidity) + this.data.chartCategories.unshift(timestamp.substring(11)) + this.setData({ + timestamp: timestamp, + chartDataTemp: this.data.chartDataTemp, + chartDataHumi: this.data.chartDataHumi, + chartCategories: this.data.chartCategories, + }) + this.renderChartTemp(400, 160) + this.renderChartHumi(400, 160) + }, + // 查询传感器设备状态 + async querySensorDeviceStatus() { + let res = await wx.cloud.callFunction({ + name: 'query', + data: config.SensorDevice + }) + console.log("sensorDevice status", res.result.Data) + let data = JSON.parse(res.result.Data) + this.setData({ + sensorDevice: data + }) + return this.data.sensorDevice + }, + // 查询执行设备状态 + async queryExecuteDeviceStatus() { + let res = await wx.cloud.callFunction({ + name: 'iothub-shadow-query', + data: config.ExecuteDevice, + }) + console.log("executeDevice status", res.result) + let deviceData = JSON.parse(res.result.Data) + // this.setData({ + // executeDevice: deviceData.payload.state.reported + // }) + return deviceData.payload.state.reported + }, + async turnOnLight() { + await this.control({ + light: 1 + }) + this.setData({ + [`executeDevice.light`]: 1 + }) + }, + async turnOffLight() { + await this.control({ + light: 0 + }) + this.setData({ + [`executeDevice.light`]: 0 + }) + }, + async turnOnFan() { + await this.control({ + motor: 1 + }) + this.setData({ + [`executeDevice.motor`]: 1 + }) + }, + async turnOffFan() { + await this.control({ + motor: 0 + }) + this.setData({ + [`executeDevice.motor`]: 0 + }) + }, + async control(obj) { + let data = config.ExecuteDevice + data.Payload = JSON.stringify(obj) + await wx.cloud.callFunction({ + name: 'iothub-publish', + data: data + }) + }, + async switchChange(e) { + let value = 0 + if (e.detail.value == true) { + value = 1 + } + let item = e.currentTarget.dataset.item + let obj = { + [`${item}`]: value + } + wx.showLoading() + let data = config.ExecuteDevice + data.Payload = JSON.stringify(obj) + console.log(data) + await wx.cloud.callFunction({ + name: 'iothub-publish', + data: data + }) + wx.hideLoading() + }, + touchHandler: function(e) { + if (!lineChart) { + return; + } + lineChart.scrollStart(e); + }, + moveHandler: function(e) { + if (!lineChart) { + return; + } + lineChart.scroll(e); + }, + touchEndHandler: function(e) { + if (!lineChart) { + return; + } + lineChart.scrollEnd(e); + lineChart.showToolTip(e, { + format: function(item, category) { + return category + ' ' + item.name + ':' + item.data + } + }); + }, + renderChartHumi: function(windowWidth, windowHeight) { + lineChart = new wxCharts({ + canvasId: 'chartHumi', + type: 'line', + categories: this.data.chartCategories, // simulationData.categories, + animation: false, + series: [{ + name: '湿度', + data: this.data.chartDataHumi, // simulationData.data2, + format: function(val, name) { + return val.toFixed(2) + "%"; + // return val + "%"; + } + }], + xAxis: { + disableGrid: false, + fontColor: '#666666', + }, + yAxis: { + // title: '湿度 (%)', + // format: function (val) { + // return val.toFixed(2); + // }, + max: 100, + min: 50, + disabled: false, + }, + width: windowWidth, + height: windowHeight, + legend: true, + dataLabel: true, + dataPointShape: true, + enableScroll: true, + extra: { + lineStyle: 'curve' + }, + }); + }, + renderChartTemp: function(windowWidth, windowHeight) { + lineChart = new wxCharts({ + canvasId: 'chartTemp', + type: 'line', + categories: this.data.chartCategories, // simulationData.categories, + animation: false, + series: [{ + name: '温度', + data: this.data.chartDataTemp, //simulationData.data, + format: function(val, name) { + return val.toFixed(2) + "℃"; + // return val + "℃"; + } + }], + xAxis: { + disableGrid: false + }, + yAxis: { + // title: '温度 (℃)', + // format: function (val) { + // return val.toFixed(2); + // }, + min: 15, + max: 25, + disabled: false, + }, + width: windowWidth, + height: windowHeight, + legend: true, + dataLabel: true, + dataPointShape: true, + enableScroll: true, + extra: { + lineStyle: 'curve' + }, + }); + }, + createSimulationData: function() { + var categories = []; + var data = []; + var data2 = []; + for (var i = 0; i < 10; i++) { + categories.push('201620162-' + (i + 1)); + data.push(Math.random() * (20 - 10) + 10); + data2.push(Math.random() * (20 - 10) + 30); + } + return { + categories: categories, + data: data, + data2: data2 + } + }, +}) \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.json b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxml b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxml new file mode 100644 index 00000000..69c61535 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxml @@ -0,0 +1,66 @@ + + + + + + + +
+ + + 传感设备 + + + {{timestamp}} + + + + + 温度 + + {{sensorDevice.temperature.Value}} + + + + + + 湿度 + + {{sensorDevice.humidity.Value}} + + % + + + + +
+ +
+ + + 执行设备 + + + + + 照明 + + + + + + 风扇 + + + + + + 温度门限 + + + +
+
+ + + \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxss b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxss new file mode 100644 index 00000000..893c8b2c --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/pages/index/index.wxss @@ -0,0 +1,32 @@ +.body { + margin: 10rpx 20rpx; +} + +.box { + padding: 0rpx 20rpx; + border-top: 2px solid #000; +} + +.cell { + margin-top: 10rpx; + margin-bottom: 20rpx; + display: flex; + justify-content: space-between; + align-items: center; +} + +.left { + width: 50%; + text-align: left; +} + +.right { + width: 40%; + text-align: right; +} + +.unit { + width: 10%; + text-align: right; +} + diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/sitemap.json b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/sitemap.json new file mode 100644 index 00000000..ca02add2 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/timeutil.wxs b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/timeutil.wxs new file mode 100644 index 00000000..2119a646 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/timeutil.wxs @@ -0,0 +1,48 @@ +var formatNumber = function (n) { + n = n.toString() + return n[1] ? n : '0' + n +} + +var regYear = getRegExp("(y+)", "i"); + +var dateFormat = function (timestamp, format) { + if (!format) { + format = "yyyy-MM-dd hh:mm:ss"; + } + timestamp = parseInt(timestamp); + var realDate = getDate(timestamp); + function timeFormat(num) { + return num < 10 ? '0' + num : num; + } + var date = [ + ["M+", timeFormat(realDate.getMonth() + 1)], + ["d+", timeFormat(realDate.getDate())], + ["h+", timeFormat(realDate.getHours())], + ["m+", timeFormat(realDate.getMinutes())], + ["s+", timeFormat(realDate.getSeconds())], + ["q+", Math.floor((realDate.getMonth() + 3) / 3)], + ["S+", realDate.getMilliseconds()], + ]; + var reg1 = regYear.exec(format); + // console.log(reg1[0]); + if (reg1) { + + format = format.replace(reg1[1], (realDate.getFullYear() + '').substring(4 - reg1[1].length)); + } + for (var i = 0; i < date.length; i++) { + var k = date[i][0]; + var v = date[i][1]; + + var reg2 = getRegExp("(" + k + ")").exec(format); + if (reg2) { + format = format.replace(reg2[1], reg2[1].length == 1 + ? v : ("00" + v).substring(("" + v).length)); + } + } + return format; +} + + +module.exports = { + dateFormat: dateFormat +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/util.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/util.js new file mode 100644 index 00000000..114726c3 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/util.js @@ -0,0 +1,33 @@ +// await util.delayMs(1000) +const delayMs = (ms) => { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + +function Appendzero(obj) { + if (obj < 10) return "0" + "" + obj; + else return obj; +} + +// epoch => datetime str +// 1578450878 => 2019-10-23 18:05:30 +function formatEpoch(epoch) { + var dateTime = new Date(parseInt(epoch) * 1000) + var year = dateTime.getFullYear(); + var month = dateTime.getMonth() + 1; + var day = dateTime.getDate(); + var hour = dateTime.getHours(); + var minute = dateTime.getMinutes(); + var second = dateTime.getSeconds(); + var now = new Date(); + var now_new = Date.parse(now.toDateString()); //typescript转换写法 + var milliseconds = now_new - dateTime; + var timeSpanStr = year + '-' + Appendzero(month) + '-' + Appendzero(day) + ' ' + Appendzero(hour) + ':' + Appendzero(minute) + ':' + Appendzero(second); + return timeSpanStr; +} + +module.exports = { + delayMs: delayMs, + formatEpoch: formatEpoch, +} \ No newline at end of file diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/wxcharts.js b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/wxcharts.js new file mode 100644 index 00000000..e222a8d8 --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/miniprogram/utils/wxcharts.js @@ -0,0 +1,2048 @@ +/* + * charts for WeChat small app v1.0 + * + * https://github.com/xiaolin3303/wx-charts + * 2016-11-28 + * + * Designed and built with all the love of Web + */ + +'use strict'; + +var config = { + yAxisWidth: 15, + yAxisSplit: 5, + xAxisHeight: 15, + xAxisLineHeight: 15, + legendHeight: 15, + yAxisTitleWidth: 15, + padding: 12, + columePadding: 0, //3 + fontSize: 10, + dataPointShape: ['diamond', 'circle', 'triangle', 'rect'], + colors: ['#7cb5ec', '#f7a35c', '#434348', '#90ed7d', '#f15c80', '#8085e9'], + pieChartLinePadding: 0, //25 + pieChartTextPadding: 0, //15 + xAxisTextPadding: 3, + titleColor: '#333333', + titleFontSize: 20, + subtitleColor: '#999999', + subtitleFontSize: 15, + toolTipPadding: 3, + toolTipBackground: '#000000', + toolTipOpacity: 0.7, + toolTipLineHeight: 14, + radarGridCount: 3, + radarLabelTextMargin: 0 // 15 +}; + +// Object.assign polyfill +// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign +function assign(target, varArgs) { + if (target == null) { + // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { + // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; +} + +var util = { + toFixed: function toFixed(num, limit) { + limit = limit || 2; + if (this.isFloat(num)) { + num = num.toFixed(limit); + } + return num; + }, + isFloat: function isFloat(num) { + return num % 1 !== 0; + }, + approximatelyEqual: function approximatelyEqual(num1, num2) { + return Math.abs(num1 - num2) < 1e-10; + }, + isSameSign: function isSameSign(num1, num2) { + return Math.abs(num1) === num1 && Math.abs(num2) === num2 || Math.abs(num1) !== num1 && Math.abs(num2) !== num2; + }, + isSameXCoordinateArea: function isSameXCoordinateArea(p1, p2) { + return this.isSameSign(p1.x, p2.x); + }, + isCollision: function isCollision(obj1, obj2) { + obj1.end = {}; + obj1.end.x = obj1.start.x + obj1.width; + obj1.end.y = obj1.start.y - obj1.height; + obj2.end = {}; + obj2.end.x = obj2.start.x + obj2.width; + obj2.end.y = obj2.start.y - obj2.height; + var flag = obj2.start.x > obj1.end.x || obj2.end.x < obj1.start.x || obj2.end.y > obj1.start.y || obj2.start.y < obj1.end.y; + + return !flag; + } +}; + +function findRange(num, type, limit) { + if (isNaN(num)) { + throw new Error('[wxCharts] unvalid series data!'); + } + limit = limit || 10; + type = type ? type : 'upper'; + var multiple = 1; + while (limit < 1) { + limit *= 10; + multiple *= 10; + } + if (type === 'upper') { + num = Math.ceil(num * multiple); + } else { + num = Math.floor(num * multiple); + } + while (num % limit !== 0) { + if (type === 'upper') { + num++; + } else { + num--; + } + } + + return num / multiple; +} + +function calValidDistance(distance, chartData, config, opts) { + + var dataChartAreaWidth = opts.width - config.padding - chartData.xAxisPoints[0]; + var dataChartWidth = chartData.eachSpacing * opts.categories.length; + var validDistance = distance; + if (distance >= 0) { + validDistance = 0; + } else if (Math.abs(distance) >= dataChartWidth - dataChartAreaWidth) { + validDistance = dataChartAreaWidth - dataChartWidth; + } + return validDistance; +} + +function isInAngleRange(angle, startAngle, endAngle) { + function adjust(angle) { + while (angle < 0) { + angle += 2 * Math.PI; + } + while (angle > 2 * Math.PI) { + angle -= 2 * Math.PI; + } + + return angle; + } + + angle = adjust(angle); + startAngle = adjust(startAngle); + endAngle = adjust(endAngle); + if (startAngle > endAngle) { + endAngle += 2 * Math.PI; + if (angle < startAngle) { + angle += 2 * Math.PI; + } + } + + return angle >= startAngle && angle <= endAngle; +} + +function calRotateTranslate(x, y, h) { + var xv = x; + var yv = h - y; + + var transX = xv + (h - yv - xv) / Math.sqrt(2); + transX *= -1; + + var transY = (h - yv) * (Math.sqrt(2) - 1) - (h - yv - xv) / Math.sqrt(2); + + return { + transX: transX, + transY: transY + }; +} + +function createCurveControlPoints(points, i) { + + function isNotMiddlePoint(points, i) { + if (points[i - 1] && points[i + 1]) { + return points[i].y >= Math.max(points[i - 1].y, points[i + 1].y) || points[i].y <= Math.min(points[i - 1].y, points[i + 1].y); + } else { + return false; + } + } + + var a = 0.2; + var b = 0.2; + var pAx = null; + var pAy = null; + var pBx = null; + var pBy = null; + if (i < 1) { + pAx = points[0].x + (points[1].x - points[0].x) * a; + pAy = points[0].y + (points[1].y - points[0].y) * a; + } else { + pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a; + pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a; + } + + if (i > points.length - 3) { + var last = points.length - 1; + pBx = points[last].x - (points[last].x - points[last - 1].x) * b; + pBy = points[last].y - (points[last].y - points[last - 1].y) * b; + } else { + pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b; + pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b; + } + + // fix issue https://github.com/xiaolin3303/wx-charts/issues/79 + if (isNotMiddlePoint(points, i + 1)) { + pBy = points[i + 1].y; + } + if (isNotMiddlePoint(points, i)) { + pAy = points[i].y; + } + + return { + ctrA: { x: pAx, y: pAy }, + ctrB: { x: pBx, y: pBy } + }; +} + +function convertCoordinateOrigin(x, y, center) { + return { + x: center.x + x, + y: center.y - y + }; +} + +function avoidCollision(obj, target) { + if (target) { + // is collision test + while (util.isCollision(obj, target)) { + if (obj.start.x > 0) { + obj.start.y--; + } else if (obj.start.x < 0) { + obj.start.y++; + } else { + if (obj.start.y > 0) { + obj.start.y++; + } else { + obj.start.y--; + } + } + } + } + return obj; +} + +function fillSeriesColor(series, config) { + var index = 0; + return series.map(function (item) { + if (!item.color) { + item.color = config.colors[index]; + index = (index + 1) % config.colors.length; + } + return item; + }); +} + +function getDataRange(minData, maxData) { + var limit = 0; + var range = maxData - minData; + if (range >= 10000) { + limit = 1000; + } else if (range >= 1000) { + limit = 100; + } else if (range >= 100) { + limit = 10; + } else if (range >= 10) { + limit = 5; + } else if (range >= 1) { + limit = 1; + } else if (range >= 0.1) { + limit = 0.1; + } else { + limit = 0.01; + } + return { + minRange: findRange(minData, 'lower', limit), + maxRange: findRange(maxData, 'upper', limit) + }; +} + +function measureText(text) { + var fontSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10; + + // wx canvas 未实现measureText方法, 此处自行实现 + text = String(text); + var text = text.split(''); + var width = 0; + text.forEach(function (item) { + if (/[a-zA-Z]/.test(item)) { + width += 7; + } else if (/[0-9]/.test(item)) { + width += 5.5; + } else if (/\./.test(item)) { + width += 2.7; + } else if (/-/.test(item)) { + width += 3.25; + } else if (/[\u4e00-\u9fa5]/.test(item)) { + width += 10; + } else if (/\(|\)/.test(item)) { + width += 3.73; + } else if (/\s/.test(item)) { + width += 2.5; + } else if (/%/.test(item)) { + width += 8; + } else { + width += 10; + } + }); + return width * fontSize / 10; +} + +function dataCombine(series) { + return series.reduce(function (a, b) { + return (a.data ? a.data : a).concat(b.data); + }, []); +} + +function getSeriesDataItem(series, index) { + var data = []; + series.forEach(function (item) { + if (item.data[index] !== null && typeof item.data[index] !== 'undefinded') { + var seriesItem = {}; + seriesItem.color = item.color; + seriesItem.name = item.name; + seriesItem.data = item.format ? item.format(item.data[index]) : item.data[index]; + data.push(seriesItem); + } + }); + + return data; +} + + + +function getMaxTextListLength(list) { + var lengthList = list.map(function (item) { + return measureText(item); + }); + return Math.max.apply(null, lengthList); +} + +function getRadarCoordinateSeries(length) { + var eachAngle = 2 * Math.PI / length; + var CoordinateSeries = []; + for (var i = 0; i < length; i++) { + CoordinateSeries.push(eachAngle * i); + } + + return CoordinateSeries.map(function (item) { + return -1 * item + Math.PI / 2; + }); +} + +function getToolTipData(seriesData, calPoints, index, categories) { + var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; + + var textList = seriesData.map(function (item) { + return { + text: option.format ? option.format(item, categories[index]) : item.name + ': ' + item.data, + color: item.color + }; + }); + var validCalPoints = []; + var offset = { + x: 0, + y: 0 + }; + calPoints.forEach(function (points) { + if (typeof points[index] !== 'undefinded' && points[index] !== null) { + validCalPoints.push(points[index]); + } + }); + validCalPoints.forEach(function (item) { + offset.x = Math.round(item.x); + offset.y += item.y; + }); + + offset.y /= validCalPoints.length; + return { textList: textList, offset: offset }; +} + +function findCurrentIndex(currentPoints, xAxisPoints, opts, config) { + var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; + + var currentIndex = -1; + if (isInExactChartArea(currentPoints, opts, config)) { + xAxisPoints.forEach(function (item, index) { + if (currentPoints.x + offset > item) { + currentIndex = index; + } + }); + } + + return currentIndex; +} + +function isInExactChartArea(currentPoints, opts, config) { + return currentPoints.x < opts.width - config.padding && currentPoints.x > config.padding + config.yAxisWidth + config.yAxisTitleWidth && currentPoints.y > config.padding && currentPoints.y < opts.height - config.legendHeight - config.xAxisHeight - config.padding; +} + +function findRadarChartCurrentIndex(currentPoints, radarData, count) { + var eachAngleArea = 2 * Math.PI / count; + var currentIndex = -1; + if (isInExactPieChartArea(currentPoints, radarData.center, radarData.radius)) { + var fixAngle = function fixAngle(angle) { + if (angle < 0) { + angle += 2 * Math.PI; + } + if (angle > 2 * Math.PI) { + angle -= 2 * Math.PI; + } + return angle; + }; + + var angle = Math.atan2(radarData.center.y - currentPoints.y, currentPoints.x - radarData.center.x); + angle = -1 * angle; + if (angle < 0) { + angle += 2 * Math.PI; + } + + var angleList = radarData.angleList.map(function (item) { + item = fixAngle(-1 * item); + + return item; + }); + + angleList.forEach(function (item, index) { + var rangeStart = fixAngle(item - eachAngleArea / 2); + var rangeEnd = fixAngle(item + eachAngleArea / 2); + if (rangeEnd < rangeStart) { + rangeEnd += 2 * Math.PI; + } + if (angle >= rangeStart && angle <= rangeEnd || angle + 2 * Math.PI >= rangeStart && angle + 2 * Math.PI <= rangeEnd) { + currentIndex = index; + } + }); + } + + return currentIndex; +} + +function findPieChartCurrentIndex(currentPoints, pieData) { + var currentIndex = -1; + if (isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) { + var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x); + angle = -angle; + for (var i = 0, len = pieData.series.length; i < len; i++) { + var item = pieData.series[i]; + if (isInAngleRange(angle, item._start_, item._start_ + item._proportion_ * 2 * Math.PI)) { + currentIndex = i; + break; + } + } + } + + return currentIndex; +} + +function isInExactPieChartArea(currentPoints, center, radius) { + return Math.pow(currentPoints.x - center.x, 2) + Math.pow(currentPoints.y - center.y, 2) <= Math.pow(radius, 2); +} + +function splitPoints(points) { + var newPoints = []; + var items = []; + points.forEach(function (item, index) { + if (item !== null) { + items.push(item); + } else { + if (items.length) { + newPoints.push(items); + } + items = []; + } + }); + if (items.length) { + newPoints.push(items); + } + + return newPoints; +} + +function calLegendData(series, opts, config) { + if (opts.legend === false) { + return { + legendList: [], + legendHeight: 0 + }; + } + var padding = 5; + var marginTop = 8; + var shapeWidth = 15; + var legendList = []; + var widthCount = 0; + var currentRow = []; + series.forEach(function (item) { + var itemWidth = 3 * padding + shapeWidth + measureText(item.name || 'undefinded'); + if (widthCount + itemWidth > opts.width) { + legendList.push(currentRow); + widthCount = itemWidth; + currentRow = [item]; + } else { + widthCount += itemWidth; + currentRow.push(item); + } + }); + if (currentRow.length) { + legendList.push(currentRow); + } + + return { + legendList: legendList, + legendHeight: legendList.length * (config.fontSize + marginTop) + padding + }; +} + +function calCategoriesData(categories, opts, config) { + var result = { + angle: 0, + xAxisHeight: config.xAxisHeight + }; + + var _getXAxisPoints = getXAxisPoints(categories, opts, config), + eachSpacing = _getXAxisPoints.eachSpacing; + + // get max length of categories text + + + var categoriesTextLenth = categories.map(function (item) { + return measureText(item); + }); + + var maxTextLength = Math.max.apply(this, categoriesTextLenth); + + if (maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) { + result.angle = 45 * Math.PI / 180; + result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle); + } + + return result; +} + +function getRadarDataPoints(angleList, center, radius, series, opts) { + var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1; + + var radarOption = opts.extra.radar || {}; + radarOption.max = radarOption.max || 0; + var maxData = Math.max(radarOption.max, Math.max.apply(null, dataCombine(series))); + + var data = []; + series.forEach(function (each) { + var listItem = {}; + listItem.color = each.color; + listItem.data = []; + each.data.forEach(function (item, index) { + var tmp = {}; + tmp.angle = angleList[index]; + + tmp.proportion = item / maxData; + tmp.position = convertCoordinateOrigin(radius * tmp.proportion * process * Math.cos(tmp.angle), radius * tmp.proportion * process * Math.sin(tmp.angle), center); + listItem.data.push(tmp); + }); + + data.push(listItem); + }); + + return data; +} + +function getPieDataPoints(series) { + var process = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; + + var count = 0; + var _start_ = 0; + series.forEach(function (item) { + item.data = item.data === null ? 0 : item.data; + count += item.data; + }); + series.forEach(function (item) { + item.data = item.data === null ? 0 : item.data; + item._proportion_ = item.data / count * process; + }); + series.forEach(function (item) { + item._start_ = _start_; + _start_ += 2 * item._proportion_ * Math.PI; + }); + + return series; +} + +function getPieTextMaxLength(series) { + series = getPieDataPoints(series); + var maxLength = 0; + series.forEach(function (item) { + var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%'; + maxLength = Math.max(maxLength, measureText(text)); + }); + + return maxLength; +} + +function fixColumeData(points, eachSpacing, columnLen, index, config, opts) { + return points.map(function (item) { + if (item === null) { + return null; + } + item.width = (eachSpacing - 2 * config.columePadding) / columnLen; + + if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) { + // customer column width + item.width = Math.min(item.width, +opts.extra.column.width); + } else { + // default width should less tran 25px + // don't ask me why, I don't know + item.width = Math.min(item.width, 25); + } + item.x += (index + 0.5 - columnLen / 2) * item.width; + + return item; + }); +} + +function getXAxisPoints(categories, opts, config) { + var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth; + var spacingValid = opts.width - 2 * config.padding - yAxisTotalWidth; + var dataCount = opts.enableScroll ? Math.min(5, categories.length) : categories.length; + var eachSpacing = spacingValid / dataCount; + + var xAxisPoints = []; + var startX = config.padding + yAxisTotalWidth; + var endX = opts.width - config.padding; + categories.forEach(function (item, index) { + xAxisPoints.push(startX + index * eachSpacing); + }); + if (opts.enableScroll === true) { + xAxisPoints.push(startX + categories.length * eachSpacing); + } else { + xAxisPoints.push(endX); + } + + return { xAxisPoints: xAxisPoints, startX: startX, endX: endX, eachSpacing: eachSpacing }; +} + +function getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) { + var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1; + + var points = []; + var validHeight = opts.height - 2 * config.padding - config.xAxisHeight - config.legendHeight; + data.forEach(function (item, index) { + if (item === null) { + points.push(null); + } else { + var point = {}; + point.x = xAxisPoints[index] + Math.round(eachSpacing / 2); + var height = validHeight * (item - minRange) / (maxRange - minRange); + height *= process; + point.y = opts.height - config.xAxisHeight - config.legendHeight - Math.round(height) - config.padding; + points.push(point); + } + }); + + return points; +} + +function getYAxisTextList(series, opts, config) { + var data = dataCombine(series); + // remove null from data + data = data.filter(function (item) { + return item !== null; + }); + var minData = Math.min.apply(this, data); + var maxData = Math.max.apply(this, data); + if (typeof opts.yAxis.min === 'number') { + minData = Math.min(opts.yAxis.min, minData); + } + if (typeof opts.yAxis.max === 'number') { + maxData = Math.max(opts.yAxis.max, maxData); + } + + // fix issue https://github.com/xiaolin3303/wx-charts/issues/9 + if (minData === maxData) { + var rangeSpan = maxData || 1; + minData -= rangeSpan; + maxData += rangeSpan; + } + + var dataRange = getDataRange(minData, maxData); + var minRange = dataRange.minRange; + var maxRange = dataRange.maxRange; + + var range = []; + var eachRange = (maxRange - minRange) / config.yAxisSplit; + + for (var i = 0; i <= config.yAxisSplit; i++) { + range.push(minRange + eachRange * i); + } + return range.reverse(); +} + +function calYAxisData(series, opts, config) { + + var ranges = getYAxisTextList(series, opts, config); + var yAxisWidth = config.yAxisWidth; + var rangesFormat = ranges.map(function (item) { + item = util.toFixed(item, 2); + item = opts.yAxis.format ? opts.yAxis.format(Number(item)) : item; + yAxisWidth = Math.max(yAxisWidth, measureText(item) + 5); + return item; + }); + if (opts.yAxis.disabled === true) { + yAxisWidth = 0; + } + + return { rangesFormat: rangesFormat, ranges: ranges, yAxisWidth: yAxisWidth }; +} + +function drawPointShape(points, color, shape, context) { + context.beginPath(); + context.setStrokeStyle("#ffffff"); + context.setLineWidth(1); + context.setFillStyle(color); + + if (shape === 'diamond') { + points.forEach(function (item, index) { + if (item !== null) { + context.moveTo(item.x, item.y - 4.5); + context.lineTo(item.x - 4.5, item.y); + context.lineTo(item.x, item.y + 4.5); + context.lineTo(item.x + 4.5, item.y); + context.lineTo(item.x, item.y - 4.5); + } + }); + } else if (shape === 'circle') { + points.forEach(function (item, index) { + if (item !== null) { + context.moveTo(item.x + 3.5, item.y); + context.arc(item.x, item.y, 4, 0, 2 * Math.PI, false); + } + }); + } else if (shape === 'rect') { + points.forEach(function (item, index) { + if (item !== null) { + context.moveTo(item.x - 3.5, item.y - 3.5); + context.rect(item.x - 3.5, item.y - 3.5, 7, 7); + } + }); + } else if (shape === 'triangle') { + points.forEach(function (item, index) { + if (item !== null) { + context.moveTo(item.x, item.y - 4.5); + context.lineTo(item.x - 4.5, item.y + 4.5); + context.lineTo(item.x + 4.5, item.y + 4.5); + context.lineTo(item.x, item.y - 4.5); + } + }); + } + context.closePath(); + context.fill(); + context.stroke(); +} + +function drawRingTitle(opts, config, context) { + var titlefontSize = opts.title.fontSize || config.titleFontSize; + var subtitlefontSize = opts.subtitle.fontSize || config.subtitleFontSize; + var title = opts.title.name || ''; + var subtitle = opts.subtitle.name || ''; + var titleFontColor = opts.title.color || config.titleColor; + var subtitleFontColor = opts.subtitle.color || config.subtitleColor; + var titleHeight = title ? titlefontSize : 0; + var subtitleHeight = subtitle ? subtitlefontSize : 0; + var margin = 5; + if (subtitle) { + var textWidth = measureText(subtitle, subtitlefontSize); + var startX = (opts.width - textWidth) / 2 + (opts.subtitle.offsetX || 0); + var startY = (opts.height - config.legendHeight + subtitlefontSize) / 2; + if (title) { + startY -= (titleHeight + margin) / 2; + } + context.beginPath(); + context.setFontSize(subtitlefontSize); + context.setFillStyle(subtitleFontColor); + context.fillText(subtitle, startX, startY); + context.stroke(); + context.closePath(); + } + if (title) { + var _textWidth = measureText(title, titlefontSize); + var _startX = (opts.width - _textWidth) / 2 + (opts.title.offsetX || 0); + var _startY = (opts.height - config.legendHeight + titlefontSize) / 2; + if (subtitle) { + _startY += (subtitleHeight + margin) / 2; + } + context.beginPath(); + context.setFontSize(titlefontSize); + context.setFillStyle(titleFontColor); + context.fillText(title, _startX, _startY); + context.stroke(); + context.closePath(); + } +} + +function drawPointText(points, series, config, context) { + // 绘制数据文案 + var data = series.data; + + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle('#666666'); + points.forEach(function (item, index) { + if (item !== null) { + var formatVal = series.format ? series.format(data[index]) : data[index]; + context.fillText(formatVal, item.x - measureText(formatVal) / 2, item.y - 2); + } + }); + context.closePath(); + context.stroke(); +} + +function drawRadarLabel(angleList, radius, centerPosition, opts, config, context) { + var radarOption = opts.extra.radar || {}; + radius += config.radarLabelTextMargin; + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle(radarOption.labelColor || '#666666'); + angleList.forEach(function (angle, index) { + var pos = { + x: radius * Math.cos(angle), + y: radius * Math.sin(angle) + }; + var posRelativeCanvas = convertCoordinateOrigin(pos.x, pos.y, centerPosition); + var startX = posRelativeCanvas.x; + var startY = posRelativeCanvas.y; + if (util.approximatelyEqual(pos.x, 0)) { + startX -= measureText(opts.categories[index] || '') / 2; + } else if (pos.x < 0) { + startX -= measureText(opts.categories[index] || ''); + } + context.fillText(opts.categories[index] || '', startX, startY + config.fontSize / 2); + }); + context.stroke(); + context.closePath(); +} + +function drawPieText(series, opts, config, context, radius, center) { + var lineRadius = radius + config.pieChartLinePadding; + var textObjectCollection = []; + var lastTextObject = null; + + var seriesConvert = series.map(function (item) { + var arc = 2 * Math.PI - (item._start_ + 2 * Math.PI * item._proportion_ / 2); + var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%'; + var color = item.color; + return { arc: arc, text: text, color: color }; + }); + seriesConvert.forEach(function (item) { + // line end + var orginX1 = Math.cos(item.arc) * lineRadius; + var orginY1 = Math.sin(item.arc) * lineRadius; + + // line start + var orginX2 = Math.cos(item.arc) * radius; + var orginY2 = Math.sin(item.arc) * radius; + + // text start + var orginX3 = orginX1 >= 0 ? orginX1 + config.pieChartTextPadding : orginX1 - config.pieChartTextPadding; + var orginY3 = orginY1; + + var textWidth = measureText(item.text); + var startY = orginY3; + + if (lastTextObject && util.isSameXCoordinateArea(lastTextObject.start, { x: orginX3 })) { + if (orginX3 > 0) { + startY = Math.min(orginY3, lastTextObject.start.y); + } else if (orginX1 < 0) { + startY = Math.max(orginY3, lastTextObject.start.y); + } else { + if (orginY3 > 0) { + startY = Math.max(orginY3, lastTextObject.start.y); + } else { + startY = Math.min(orginY3, lastTextObject.start.y); + } + } + } + + if (orginX3 < 0) { + orginX3 -= textWidth; + } + + var textObject = { + lineStart: { + x: orginX2, + y: orginY2 + }, + lineEnd: { + x: orginX1, + y: orginY1 + }, + start: { + x: orginX3, + y: startY + }, + width: textWidth, + height: config.fontSize, + text: item.text, + color: item.color + }; + + lastTextObject = avoidCollision(textObject, lastTextObject); + textObjectCollection.push(lastTextObject); + }); + + textObjectCollection.forEach(function (item) { + var lineStartPoistion = convertCoordinateOrigin(item.lineStart.x, item.lineStart.y, center); + var lineEndPoistion = convertCoordinateOrigin(item.lineEnd.x, item.lineEnd.y, center); + var textPosition = convertCoordinateOrigin(item.start.x, item.start.y, center); + context.setLineWidth(1); + context.setFontSize(config.fontSize); + context.beginPath(); + context.setStrokeStyle(item.color); + context.setFillStyle(item.color); + context.moveTo(lineStartPoistion.x, lineStartPoistion.y); + var curveStartX = item.start.x < 0 ? textPosition.x + item.width : textPosition.x; + var textStartX = item.start.x < 0 ? textPosition.x - 5 : textPosition.x + 5; + context.quadraticCurveTo(lineEndPoistion.x, lineEndPoistion.y, curveStartX, textPosition.y); + context.moveTo(lineStartPoistion.x, lineStartPoistion.y); + context.stroke(); + context.closePath(); + context.beginPath(); + context.moveTo(textPosition.x + item.width, textPosition.y); + context.arc(curveStartX, textPosition.y, 2, 0, 2 * Math.PI); + context.closePath(); + context.fill(); + context.beginPath(); + context.setFillStyle('#666666'); + context.fillText(item.text, textStartX, textPosition.y + 3); + context.closePath(); + context.stroke(); + + context.closePath(); + }); +} + +function drawToolTipSplitLine(offsetX, opts, config, context) { + var startY = config.padding; + var endY = opts.height - config.padding - config.xAxisHeight - config.legendHeight; + context.beginPath(); + context.setStrokeStyle('#cccccc'); + context.setLineWidth(1); + context.moveTo(offsetX, startY); + context.lineTo(offsetX, endY); + context.stroke(); + context.closePath(); +} + +function drawToolTip(textList, offset, opts, config, context) { + var legendWidth = 4; + var legendMarginRight = 5; + var arrowWidth = 8; + var isOverRightBorder = false; + offset = assign({ + x: 0, + y: 0 + }, offset); + offset.y -= 8; + var textWidth = textList.map(function (item) { + return measureText(item.text); + }); + + var toolTipWidth = legendWidth + legendMarginRight + 4 * config.toolTipPadding + Math.max.apply(null, textWidth); + var toolTipHeight = 2 * config.toolTipPadding + textList.length * config.toolTipLineHeight; + + // if beyond the right border + if (offset.x - Math.abs(opts._scrollDistance_) + arrowWidth + toolTipWidth > opts.width) { + isOverRightBorder = true; + } + + // draw background rect + context.beginPath(); + context.setFillStyle(opts.tooltip.option.background || config.toolTipBackground); + context.setGlobalAlpha(config.toolTipOpacity); + if (isOverRightBorder) { + context.moveTo(offset.x, offset.y + 10); + context.lineTo(offset.x - arrowWidth, offset.y + 10 - 5); + context.lineTo(offset.x - arrowWidth, offset.y + 10 + 5); + context.moveTo(offset.x, offset.y + 10); + context.fillRect(offset.x - toolTipWidth - arrowWidth, offset.y, toolTipWidth, toolTipHeight); + } else { + context.moveTo(offset.x, offset.y + 10); + context.lineTo(offset.x + arrowWidth, offset.y + 10 - 5); + context.lineTo(offset.x + arrowWidth, offset.y + 10 + 5); + context.moveTo(offset.x, offset.y + 10); + context.fillRect(offset.x + arrowWidth, offset.y, toolTipWidth, toolTipHeight); + } + + context.closePath(); + context.fill(); + context.setGlobalAlpha(1); + + // draw legend + textList.forEach(function (item, index) { + context.beginPath(); + context.setFillStyle(item.color); + var startX = offset.x + arrowWidth + 2 * config.toolTipPadding; + var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index + config.toolTipPadding; + if (isOverRightBorder) { + startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding; + } + context.fillRect(startX, startY, legendWidth, config.fontSize); + context.closePath(); + }); + + // draw text list + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle('#ffffff'); + textList.forEach(function (item, index) { + var startX = offset.x + arrowWidth + 2 * config.toolTipPadding + legendWidth + legendMarginRight; + if (isOverRightBorder) { + startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding + +legendWidth + legendMarginRight; + } + var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index + config.toolTipPadding; + context.fillText(item.text, startX, startY + config.fontSize); + }); + context.stroke(); + context.closePath(); +} + +function drawYAxisTitle(title, opts, config, context) { + var startX = config.xAxisHeight + (opts.height - config.xAxisHeight - measureText(title)) / 2; + context.save(); + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle(opts.yAxis.titleFontColor || '#333333'); + context.translate(0, opts.height); + context.rotate(-90 * Math.PI / 180); + context.fillText(title, startX, config.padding + 0.5 * config.fontSize); + context.stroke(); + context.closePath(); + context.restore(); +} + +function drawColumnDataPoints(series, opts, config, context) { + var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1; + + var _calYAxisData = calYAxisData(series, opts, config), + ranges = _calYAxisData.ranges; + + var _getXAxisPoints = getXAxisPoints(opts.categories, opts, config), + xAxisPoints = _getXAxisPoints.xAxisPoints, + eachSpacing = _getXAxisPoints.eachSpacing; + + var minRange = ranges.pop(); + var maxRange = ranges.shift(); + context.save(); + if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) { + context.translate(opts._scrollDistance_, 0); + } + + series.forEach(function (eachSeries, seriesIndex) { + var data = eachSeries.data; + var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process); + points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts); + + // 绘制柱状数据图 + context.beginPath(); + context.setFillStyle(eachSeries.color); + points.forEach(function (item, index) { + if (item !== null) { + var startX = item.x - item.width / 2 + 1; + var height = opts.height - item.y - config.padding - config.xAxisHeight - config.legendHeight; + context.moveTo(startX, item.y); + context.rect(startX, item.y, item.width - 2, height); + } + }); + context.closePath(); + context.fill(); + }); + series.forEach(function (eachSeries, seriesIndex) { + var data = eachSeries.data; + var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process); + points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts); + if (opts.dataLabel !== false && process === 1) { + drawPointText(points, eachSeries, config, context); + } + }); + context.restore(); + return { + xAxisPoints: xAxisPoints, + eachSpacing: eachSpacing + }; +} + +function drawAreaDataPoints(series, opts, config, context) { + var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1; + + var _calYAxisData2 = calYAxisData(series, opts, config), + ranges = _calYAxisData2.ranges; + + var _getXAxisPoints2 = getXAxisPoints(opts.categories, opts, config), + xAxisPoints = _getXAxisPoints2.xAxisPoints, + eachSpacing = _getXAxisPoints2.eachSpacing; + + var minRange = ranges.pop(); + var maxRange = ranges.shift(); + var endY = opts.height - config.padding - config.xAxisHeight - config.legendHeight; + var calPoints = []; + + context.save(); + if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) { + context.translate(opts._scrollDistance_, 0); + } + + if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) { + drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context); + } + + series.forEach(function (eachSeries, seriesIndex) { + var data = eachSeries.data; + var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process); + calPoints.push(points); + + var splitPointList = splitPoints(points); + + splitPointList.forEach(function (points) { + // 绘制区域数据 + context.beginPath(); + context.setStrokeStyle(eachSeries.color); + context.setFillStyle(eachSeries.color); + context.setGlobalAlpha(0.6); + context.setLineWidth(2); + if (points.length > 1) { + var firstPoint = points[0]; + var lastPoint = points[points.length - 1]; + + context.moveTo(firstPoint.x, firstPoint.y); + if (opts.extra.lineStyle === 'curve') { + points.forEach(function (item, index) { + if (index > 0) { + var ctrlPoint = createCurveControlPoints(points, index - 1); + context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y); + } + }); + } else { + points.forEach(function (item, index) { + if (index > 0) { + context.lineTo(item.x, item.y); + } + }); + } + + context.lineTo(lastPoint.x, endY); + context.lineTo(firstPoint.x, endY); + context.lineTo(firstPoint.x, firstPoint.y); + } else { + var item = points[0]; + context.moveTo(item.x - eachSpacing / 2, item.y); + context.lineTo(item.x + eachSpacing / 2, item.y); + context.lineTo(item.x + eachSpacing / 2, endY); + context.lineTo(item.x - eachSpacing / 2, endY); + context.moveTo(item.x - eachSpacing / 2, item.y); + } + context.closePath(); + context.fill(); + context.setGlobalAlpha(1); + }); + + if (opts.dataPointShape !== false) { + var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length]; + drawPointShape(points, eachSeries.color, shape, context); + } + }); + if (opts.dataLabel !== false && process === 1) { + series.forEach(function (eachSeries, seriesIndex) { + var data = eachSeries.data; + var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process); + drawPointText(points, eachSeries, config, context); + }); + } + + context.restore(); + + return { + xAxisPoints: xAxisPoints, + calPoints: calPoints, + eachSpacing: eachSpacing + }; +} + +function drawLineDataPoints(series, opts, config, context) { + var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1; + + var _calYAxisData3 = calYAxisData(series, opts, config), + ranges = _calYAxisData3.ranges; + + var _getXAxisPoints3 = getXAxisPoints(opts.categories, opts, config), + xAxisPoints = _getXAxisPoints3.xAxisPoints, + eachSpacing = _getXAxisPoints3.eachSpacing; + + var minRange = ranges.pop(); + var maxRange = ranges.shift(); + var calPoints = []; + + context.save(); + if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) { + context.translate(opts._scrollDistance_, 0); + } + + if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) { + drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context); + } + + series.forEach(function (eachSeries, seriesIndex) { + var data = eachSeries.data; + var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process); + calPoints.push(points); + var splitPointList = splitPoints(points); + + splitPointList.forEach(function (points, index) { + context.beginPath(); + context.setStrokeStyle(eachSeries.color); + context.setLineWidth(2); + if (points.length === 1) { + context.moveTo(points[0].x, points[0].y); + context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI); + } else { + context.moveTo(points[0].x, points[0].y); + if (opts.extra.lineStyle === 'curve') { + points.forEach(function (item, index) { + if (index > 0) { + var ctrlPoint = createCurveControlPoints(points, index - 1); + context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y); + } + }); + } else { + points.forEach(function (item, index) { + if (index > 0) { + context.lineTo(item.x, item.y); + } + }); + } + context.moveTo(points[0].x, points[0].y); + } + context.closePath(); + context.stroke(); + }); + + if (opts.dataPointShape !== false) { + var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length]; + drawPointShape(points, eachSeries.color, shape, context); + } + }); + if (opts.dataLabel !== false && process === 1) { + series.forEach(function (eachSeries, seriesIndex) { + var data = eachSeries.data; + var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process); + drawPointText(points, eachSeries, config, context); + }); + } + + context.restore(); + + return { + xAxisPoints: xAxisPoints, + calPoints: calPoints, + eachSpacing: eachSpacing + }; +} + +function drawToolTipBridge(opts, config, context, process) { + context.save(); + if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) { + context.translate(opts._scrollDistance_, 0); + } + if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) { + drawToolTip(opts.tooltip.textList, opts.tooltip.offset, opts, config, context); + } + context.restore(); +} + +function drawXAxis(categories, opts, config, context) { + var _getXAxisPoints4 = getXAxisPoints(categories, opts, config), + xAxisPoints = _getXAxisPoints4.xAxisPoints, + startX = _getXAxisPoints4.startX, + endX = _getXAxisPoints4.endX, + eachSpacing = _getXAxisPoints4.eachSpacing; + + var startY = opts.height - config.padding - config.xAxisHeight - config.legendHeight; + var endY = startY + config.xAxisLineHeight; + + context.save(); + if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) { + context.translate(opts._scrollDistance_, 0); + } + + context.beginPath(); + context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc"); + + if (opts.xAxis.disableGrid !== true) { + if (opts.xAxis.type === 'calibration') { + xAxisPoints.forEach(function (item, index) { + if (index > 0) { + context.moveTo(item - eachSpacing / 2, startY); + context.lineTo(item - eachSpacing / 2, startY + 4); + } + }); + } else { + xAxisPoints.forEach(function (item, index) { + context.moveTo(item, startY); + context.lineTo(item, endY); + }); + } + } + context.closePath(); + context.stroke(); + + // 对X轴列表做抽稀处理 + var validWidth = opts.width - 2 * config.padding - config.yAxisWidth - config.yAxisTitleWidth; + var maxXAxisListLength = Math.min(categories.length, Math.ceil(validWidth / config.fontSize / 1.5)); + var ratio = Math.ceil(categories.length / maxXAxisListLength); + + categories = categories.map(function (item, index) { + return index % ratio !== 0 ? '' : item; + }); + + if (config._xAxisTextAngle_ === 0) { + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle(opts.xAxis.fontColor || '#666666'); + categories.forEach(function (item, index) { + var offset = eachSpacing / 2 - measureText(item) / 2; + context.fillText(item, xAxisPoints[index] + offset, startY + config.fontSize + 5); + }); + context.closePath(); + context.stroke(); + } else { + categories.forEach(function (item, index) { + context.save(); + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle(opts.xAxis.fontColor || '#666666'); + var textWidth = measureText(item); + var offset = eachSpacing / 2 - textWidth; + + var _calRotateTranslate = calRotateTranslate(xAxisPoints[index] + eachSpacing / 2, startY + config.fontSize / 2 + 5, opts.height), + transX = _calRotateTranslate.transX, + transY = _calRotateTranslate.transY; + + context.rotate(-1 * config._xAxisTextAngle_); + context.translate(transX, transY); + context.fillText(item, xAxisPoints[index] + offset, startY + config.fontSize + 5); + context.closePath(); + context.stroke(); + context.restore(); + }); + } + + context.restore(); +} + +function drawYAxisGrid(opts, config, context) { + var spacingValid = opts.height - 2 * config.padding - config.xAxisHeight - config.legendHeight; + var eachSpacing = Math.floor(spacingValid / config.yAxisSplit); + var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth; + var startX = config.padding + yAxisTotalWidth; + var endX = opts.width - config.padding; + + var points = []; + for (var i = 0; i < config.yAxisSplit; i++) { + points.push(config.padding + eachSpacing * i); + } + points.push(config.padding + eachSpacing * config.yAxisSplit + 2); + + context.beginPath(); + context.setStrokeStyle(opts.yAxis.gridColor || "#cccccc"); + context.setLineWidth(1); + points.forEach(function (item, index) { + context.moveTo(startX, item); + context.lineTo(endX, item); + }); + context.closePath(); + context.stroke(); +} + +function drawYAxis(series, opts, config, context) { + if (opts.yAxis.disabled === true) { + return; + } + + var _calYAxisData4 = calYAxisData(series, opts, config), + rangesFormat = _calYAxisData4.rangesFormat; + + var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth; + + var spacingValid = opts.height - 2 * config.padding - config.xAxisHeight - config.legendHeight; + var eachSpacing = Math.floor(spacingValid / config.yAxisSplit); + var startX = config.padding + yAxisTotalWidth; + var endX = opts.width - config.padding; + var endY = opts.height - config.padding - config.xAxisHeight - config.legendHeight; + + // set YAxis background + context.setFillStyle(opts.background || '#ffffff'); + if (opts._scrollDistance_ < 0) { + context.fillRect(0, 0, startX, endY + config.xAxisHeight + 5); + } + context.fillRect(endX, 0, opts.width, endY + config.xAxisHeight + 5); + + var points = []; + for (var i = 0; i <= config.yAxisSplit; i++) { + points.push(config.padding + eachSpacing * i); + } + + context.stroke(); + context.beginPath(); + context.setFontSize(config.fontSize); + context.setFillStyle(opts.yAxis.fontColor || '#666666'); + rangesFormat.forEach(function (item, index) { + var pos = points[index] ? points[index] : endY; + context.fillText(item, config.padding + config.yAxisTitleWidth, pos + config.fontSize / 2); + }); + context.closePath(); + context.stroke(); + + if (opts.yAxis.title) { + drawYAxisTitle(opts.yAxis.title, opts, config, context); + } +} + +function drawLegend(series, opts, config, context) { + if (!opts.legend) { + return; + } + // each legend shape width 15px + // the spacing between shape and text in each legend is the `padding` + // each legend spacing is the `padding` + // legend margin top `config.padding` + + var _calLegendData = calLegendData(series, opts, config), + legendList = _calLegendData.legendList; + + var padding = 5; + var marginTop = 8; + var shapeWidth = 15; + legendList.forEach(function (itemList, listIndex) { + var width = 0; + itemList.forEach(function (item) { + item.name = item.name || 'undefined'; + width += 3 * padding + measureText(item.name) + shapeWidth; + }); + var startX = (opts.width - width) / 2 + padding; + var startY = opts.height - config.padding - config.legendHeight + listIndex * (config.fontSize + marginTop) + padding + marginTop; + + context.setFontSize(config.fontSize); + itemList.forEach(function (item) { + switch (opts.type) { + case 'line': + context.beginPath(); + context.setLineWidth(1); + context.setStrokeStyle(item.color); + context.moveTo(startX - 2, startY + 5); + context.lineTo(startX + 17, startY + 5); + context.stroke(); + context.closePath(); + context.beginPath(); + context.setLineWidth(1); + context.setStrokeStyle('#ffffff'); + context.setFillStyle(item.color); + context.moveTo(startX + 7.5, startY + 5); + context.arc(startX + 7.5, startY + 5, 4, 0, 2 * Math.PI); + context.fill(); + context.stroke(); + context.closePath(); + break; + case 'pie': + case 'ring': + context.beginPath(); + context.setFillStyle(item.color); + context.moveTo(startX + 7.5, startY + 5); + context.arc(startX + 7.5, startY + 5, 7, 0, 2 * Math.PI); + context.closePath(); + context.fill(); + break; + default: + context.beginPath(); + context.setFillStyle(item.color); + context.moveTo(startX, startY); + context.rect(startX, startY, 15, 10); + context.closePath(); + context.fill(); + } + startX += padding + shapeWidth; + context.beginPath(); + context.setFillStyle(opts.extra.legendTextColor || '#333333'); + context.fillText(item.name, startX, startY + 9); + context.closePath(); + context.stroke(); + startX += measureText(item.name) + 2 * padding; + }); + }); +} +function drawPieDataPoints(series, opts, config, context) { + var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1; + + var pieOption = opts.extra.pie || {}; + series = getPieDataPoints(series, process); + var centerPosition = { + x: opts.width / 2, + y: (opts.height - config.legendHeight) / 2 + }; + var radius = Math.min(centerPosition.x - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, centerPosition.y - config.pieChartLinePadding - config.pieChartTextPadding); + if (opts.dataLabel) { + radius -= 10; + } else { + radius -= 2 * config.padding; + } + series = series.map(function (eachSeries) { + eachSeries._start_ += (pieOption.offsetAngle || 0) * Math.PI / 180; + return eachSeries; + }); + series.forEach(function (eachSeries) { + context.beginPath(); + context.setLineWidth(2); + context.setStrokeStyle('#ffffff'); + context.setFillStyle(eachSeries.color); + context.moveTo(centerPosition.x, centerPosition.y); + context.arc(centerPosition.x, centerPosition.y, radius, eachSeries._start_, eachSeries._start_ + 2 * eachSeries._proportion_ * Math.PI); + context.closePath(); + context.fill(); + if (opts.disablePieStroke !== true) { + context.stroke(); + } + }); + + if (opts.type === 'ring') { + var innerPieWidth = radius * 0.6; + if (typeof opts.extra.ringWidth === 'number' && opts.extra.ringWidth > 0) { + innerPieWidth = Math.max(0, radius - opts.extra.ringWidth); + } + context.beginPath(); + context.setFillStyle(opts.background || '#ffffff'); + context.moveTo(centerPosition.x, centerPosition.y); + context.arc(centerPosition.x, centerPosition.y, innerPieWidth, 0, 2 * Math.PI); + context.closePath(); + context.fill(); + } + + if (opts.dataLabel !== false && process === 1) { + // fix https://github.com/xiaolin3303/wx-charts/issues/132 + var valid = false; + for (var i = 0, len = series.length; i < len; i++) { + if (series[i].data > 0) { + valid = true; + break; + } + } + + if (valid) { + drawPieText(series, opts, config, context, radius, centerPosition); + } + } + + if (process === 1 && opts.type === 'ring') { + drawRingTitle(opts, config, context); + } + + return { + center: centerPosition, + radius: radius, + series: series + }; +} + +function drawRadarDataPoints(series, opts, config, context) { + var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1; + + var radarOption = opts.extra.radar || {}; + var coordinateAngle = getRadarCoordinateSeries(opts.categories.length); + var centerPosition = { + x: opts.width / 2, + y: (opts.height - config.legendHeight) / 2 + }; + + var radius = Math.min(centerPosition.x - (getMaxTextListLength(opts.categories) + config.radarLabelTextMargin), centerPosition.y - config.radarLabelTextMargin); + + radius -= config.padding; + + // draw grid + context.beginPath(); + context.setLineWidth(1); + context.setStrokeStyle(radarOption.gridColor || "#cccccc"); + coordinateAngle.forEach(function (angle) { + var pos = convertCoordinateOrigin(radius * Math.cos(angle), radius * Math.sin(angle), centerPosition); + context.moveTo(centerPosition.x, centerPosition.y); + context.lineTo(pos.x, pos.y); + }); + context.stroke(); + context.closePath(); + + // draw split line grid + + var _loop = function _loop(i) { + var startPos = {}; + context.beginPath(); + context.setLineWidth(1); + context.setStrokeStyle(radarOption.gridColor || "#cccccc"); + coordinateAngle.forEach(function (angle, index) { + var pos = convertCoordinateOrigin(radius / config.radarGridCount * i * Math.cos(angle), radius / config.radarGridCount * i * Math.sin(angle), centerPosition); + if (index === 0) { + startPos = pos; + context.moveTo(pos.x, pos.y); + } else { + context.lineTo(pos.x, pos.y); + } + }); + context.lineTo(startPos.x, startPos.y); + context.stroke(); + context.closePath(); + }; + + for (var i = 1; i <= config.radarGridCount; i++) { + _loop(i); + } + + var radarDataPoints = getRadarDataPoints(coordinateAngle, centerPosition, radius, series, opts, process); + radarDataPoints.forEach(function (eachSeries, seriesIndex) { + // 绘制区域数据 + context.beginPath(); + context.setFillStyle(eachSeries.color); + context.setGlobalAlpha(0.6); + eachSeries.data.forEach(function (item, index) { + if (index === 0) { + context.moveTo(item.position.x, item.position.y); + } else { + context.lineTo(item.position.x, item.position.y); + } + }); + context.closePath(); + context.fill(); + context.setGlobalAlpha(1); + + if (opts.dataPointShape !== false) { + var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length]; + var points = eachSeries.data.map(function (item) { + return item.position; + }); + drawPointShape(points, eachSeries.color, shape, context); + } + }); + // draw label text + drawRadarLabel(coordinateAngle, radius, centerPosition, opts, config, context); + + return { + center: centerPosition, + radius: radius, + angleList: coordinateAngle + }; +} + +function drawCanvas(opts, context) { + context.draw(); +} + +var Timing = { + easeIn: function easeIn(pos) { + return Math.pow(pos, 3); + }, + + easeOut: function easeOut(pos) { + return Math.pow(pos - 1, 3) + 1; + }, + + easeInOut: function easeInOut(pos) { + if ((pos /= 0.5) < 1) { + return 0.5 * Math.pow(pos, 3); + } else { + return 0.5 * (Math.pow(pos - 2, 3) + 2); + } + }, + + linear: function linear(pos) { + return pos; + } +}; + +function Animation(opts) { + this.isStop = false; + opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration; + opts.timing = opts.timing || 'linear'; + + var delay = 17; + + var createAnimationFrame = function createAnimationFrame() { + if (typeof requestAnimationFrame !== 'undefined') { + return requestAnimationFrame; + } else if (typeof setTimeout !== 'undefined') { + return function (step, delay) { + setTimeout(function () { + var timeStamp = +new Date(); + step(timeStamp); + }, delay); + }; + } else { + return function (step) { + step(null); + }; + } + }; + var animationFrame = createAnimationFrame(); + var startTimeStamp = null; + var _step = function step(timestamp) { + if (timestamp === null || this.isStop === true) { + opts.onProcess && opts.onProcess(1); + opts.onAnimationFinish && opts.onAnimationFinish(); + return; + } + if (startTimeStamp === null) { + startTimeStamp = timestamp; + } + if (timestamp - startTimeStamp < opts.duration) { + var process = (timestamp - startTimeStamp) / opts.duration; + var timingFunction = Timing[opts.timing]; + process = timingFunction(process); + opts.onProcess && opts.onProcess(process); + animationFrame(_step, delay); + } else { + opts.onProcess && opts.onProcess(1); + opts.onAnimationFinish && opts.onAnimationFinish(); + } + }; + _step = _step.bind(this); + + animationFrame(_step, delay); +} + +// stop animation immediately +// and tigger onAnimationFinish +Animation.prototype.stop = function () { + this.isStop = true; +}; + +function drawCharts(type, opts, config, context) { + var _this = this; + + var series = opts.series; + var categories = opts.categories; + series = fillSeriesColor(series, config); + + var _calLegendData = calLegendData(series, opts, config), + legendHeight = _calLegendData.legendHeight; + + config.legendHeight = legendHeight; + + var _calYAxisData = calYAxisData(series, opts, config), + yAxisWidth = _calYAxisData.yAxisWidth; + + config.yAxisWidth = yAxisWidth; + if (categories && categories.length) { + var _calCategoriesData = calCategoriesData(categories, opts, config), + xAxisHeight = _calCategoriesData.xAxisHeight, + angle = _calCategoriesData.angle; + + config.xAxisHeight = xAxisHeight; + config._xAxisTextAngle_ = angle; + } + if (type === 'pie' || type === 'ring') { + config._pieTextMaxLength_ = opts.dataLabel === false ? 0 : getPieTextMaxLength(series); + } + + var duration = opts.animation ? 1000 : 0; + this.animationInstance && this.animationInstance.stop(); + switch (type) { + case 'line': + this.animationInstance = new Animation({ + timing: 'easeIn', + duration: duration, + onProcess: function onProcess(process) { + drawYAxisGrid(opts, config, context); + + var _drawLineDataPoints = drawLineDataPoints(series, opts, config, context, process), + xAxisPoints = _drawLineDataPoints.xAxisPoints, + calPoints = _drawLineDataPoints.calPoints, + eachSpacing = _drawLineDataPoints.eachSpacing; + + _this.chartData.xAxisPoints = xAxisPoints; + _this.chartData.calPoints = calPoints; + _this.chartData.eachSpacing = eachSpacing; + drawXAxis(categories, opts, config, context); + drawLegend(opts.series, opts, config, context); + drawYAxis(series, opts, config, context); + drawToolTipBridge(opts, config, context, process); + drawCanvas(opts, context); + }, + onAnimationFinish: function onAnimationFinish() { + _this.event.trigger('renderComplete'); + } + }); + break; + case 'column': + this.animationInstance = new Animation({ + timing: 'easeIn', + duration: duration, + onProcess: function onProcess(process) { + drawYAxisGrid(opts, config, context); + + var _drawColumnDataPoints = drawColumnDataPoints(series, opts, config, context, process), + xAxisPoints = _drawColumnDataPoints.xAxisPoints, + eachSpacing = _drawColumnDataPoints.eachSpacing; + + _this.chartData.xAxisPoints = xAxisPoints; + _this.chartData.eachSpacing = eachSpacing; + drawXAxis(categories, opts, config, context); + drawLegend(opts.series, opts, config, context); + drawYAxis(series, opts, config, context); + drawCanvas(opts, context); + }, + onAnimationFinish: function onAnimationFinish() { + _this.event.trigger('renderComplete'); + } + }); + break; + case 'area': + this.animationInstance = new Animation({ + timing: 'easeIn', + duration: duration, + onProcess: function onProcess(process) { + drawYAxisGrid(opts, config, context); + + var _drawAreaDataPoints = drawAreaDataPoints(series, opts, config, context, process), + xAxisPoints = _drawAreaDataPoints.xAxisPoints, + calPoints = _drawAreaDataPoints.calPoints, + eachSpacing = _drawAreaDataPoints.eachSpacing; + + _this.chartData.xAxisPoints = xAxisPoints; + _this.chartData.calPoints = calPoints; + _this.chartData.eachSpacing = eachSpacing; + drawXAxis(categories, opts, config, context); + drawLegend(opts.series, opts, config, context); + drawYAxis(series, opts, config, context); + drawToolTipBridge(opts, config, context, process); + drawCanvas(opts, context); + }, + onAnimationFinish: function onAnimationFinish() { + _this.event.trigger('renderComplete'); + } + }); + break; + case 'ring': + case 'pie': + this.animationInstance = new Animation({ + timing: 'easeInOut', + duration: duration, + onProcess: function onProcess(process) { + _this.chartData.pieData = drawPieDataPoints(series, opts, config, context, process); + drawLegend(opts.series, opts, config, context); + drawCanvas(opts, context); + }, + onAnimationFinish: function onAnimationFinish() { + _this.event.trigger('renderComplete'); + } + }); + break; + case 'radar': + this.animationInstance = new Animation({ + timing: 'easeInOut', + duration: duration, + onProcess: function onProcess(process) { + _this.chartData.radarData = drawRadarDataPoints(series, opts, config, context, process); + drawLegend(opts.series, opts, config, context); + drawCanvas(opts, context); + }, + onAnimationFinish: function onAnimationFinish() { + _this.event.trigger('renderComplete'); + } + }); + break; + } +} + +// simple event implement + +function Event() { + this.events = {}; +} + +Event.prototype.addEventListener = function (type, listener) { + this.events[type] = this.events[type] || []; + this.events[type].push(listener); +}; + +Event.prototype.trigger = function () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var type = args[0]; + var params = args.slice(1); + if (!!this.events[type]) { + this.events[type].forEach(function (listener) { + try { + listener.apply(null, params); + } catch (e) { + console.error(e); + } + }); + } +}; + +var Charts = function Charts(opts) { + opts.title = opts.title || {}; + opts.subtitle = opts.subtitle || {}; + opts.yAxis = opts.yAxis || {}; + opts.xAxis = opts.xAxis || {}; + opts.extra = opts.extra || {}; + opts.legend = opts.legend === false ? false : true; + opts.animation = opts.animation === false ? false : true; + var config$$1 = assign({}, config); + config$$1.yAxisTitleWidth = opts.yAxis.disabled !== true && opts.yAxis.title ? config$$1.yAxisTitleWidth : 0; + config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : config$$1.pieChartLinePadding; + config$$1.pieChartTextPadding = opts.dataLabel === false ? 0 : config$$1.pieChartTextPadding; + + this.opts = opts; + this.config = config$$1; + this.context = wx.createCanvasContext(opts.canvasId); + // store calcuated chart data + // such as chart point coordinate + this.chartData = {}; + this.event = new Event(); + this.scrollOption = { + currentOffset: 0, + startTouchX: 0, + distance: 0 + }; + + drawCharts.call(this, opts.type, opts, config$$1, this.context); +}; + +Charts.prototype.updateData = function () { + var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + this.opts.series = data.series || this.opts.series; + this.opts.categories = data.categories || this.opts.categories; + + this.opts.title = assign({}, this.opts.title, data.title || {}); + this.opts.subtitle = assign({}, this.opts.subtitle, data.subtitle || {}); + + drawCharts.call(this, this.opts.type, this.opts, this.config, this.context); +}; + +Charts.prototype.stopAnimation = function () { + this.animationInstance && this.animationInstance.stop(); +}; + +Charts.prototype.addEventListener = function (type, listener) { + this.event.addEventListener(type, listener); +}; + +Charts.prototype.getCurrentDataIndex = function (e) { + var touches = e.touches && e.touches.length ? e.touches : e.changedTouches; + if (touches && touches.length) { + var _touches$ = touches[0], + x = _touches$.x, + y = _touches$.y; + + if (this.opts.type === 'pie' || this.opts.type === 'ring') { + return findPieChartCurrentIndex({ x: x, y: y }, this.chartData.pieData); + } else if (this.opts.type === 'radar') { + return findRadarChartCurrentIndex({ x: x, y: y }, this.chartData.radarData, this.opts.categories.length); + } else { + return findCurrentIndex({ x: x, y: y }, this.chartData.xAxisPoints, this.opts, this.config, Math.abs(this.scrollOption.currentOffset)); + } + } + return -1; +}; + +Charts.prototype.showToolTip = function (e) { + var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + + if (this.opts.type === 'line' || this.opts.type === 'area') { + var index = this.getCurrentDataIndex(e); + var currentOffset = this.scrollOption.currentOffset; + + var opts = assign({}, this.opts, { + _scrollDistance_: currentOffset, + animation: false + }); + if (index > -1) { + var seriesData = getSeriesDataItem(this.opts.series, index); + if (seriesData.length === 0) { + drawCharts.call(this, opts.type, opts, this.config, this.context); + } else { + var _getToolTipData = getToolTipData(seriesData, this.chartData.calPoints, index, this.opts.categories, option), + textList = _getToolTipData.textList, + offset = _getToolTipData.offset; + + opts.tooltip = { + textList: textList, + offset: offset, + option: option + }; + drawCharts.call(this, opts.type, opts, this.config, this.context); + } + } else { + drawCharts.call(this, opts.type, opts, this.config, this.context); + } + } +}; + +Charts.prototype.scrollStart = function (e) { + if (e.touches[0] && this.opts.enableScroll === true) { + this.scrollOption.startTouchX = e.touches[0].x; + } +}; + +Charts.prototype.scroll = function (e) { + // TODO throtting... + if (e.touches[0] && this.opts.enableScroll === true) { + var _distance = e.touches[0].x - this.scrollOption.startTouchX; + var currentOffset = this.scrollOption.currentOffset; + + var validDistance = calValidDistance(currentOffset + _distance, this.chartData, this.config, this.opts); + + this.scrollOption.distance = _distance = validDistance - currentOffset; + var opts = assign({}, this.opts, { + _scrollDistance_: currentOffset + _distance, + animation: false + }); + + drawCharts.call(this, opts.type, opts, this.config, this.context); + } +}; + +Charts.prototype.scrollEnd = function (e) { + if (this.opts.enableScroll === true) { + var _scrollOption = this.scrollOption, + currentOffset = _scrollOption.currentOffset, + distance = _scrollOption.distance; + + this.scrollOption.currentOffset = currentOffset + distance; + this.scrollOption.distance = 0; + } +}; + +module.exports = Charts; diff --git a/tools/Mini_Program/qcloud_device_linkage_demo/project.config.json b/tools/Mini_Program/qcloud_device_linkage_demo/project.config.json new file mode 100644 index 00000000..f909d1de --- /dev/null +++ b/tools/Mini_Program/qcloud_device_linkage_demo/project.config.json @@ -0,0 +1,52 @@ +{ + "miniprogramRoot": "miniprogram/", + "cloudfunctionRoot": "cloudfunctions/", + "setting": { + "urlCheck": false, + "es6": true, + "postcss": true, + "minified": true, + "newFeature": true, + "coverView": true, + "nodeModules": true, + "autoAudits": false, + "showShadowRootInWxmlPanel": true, + "scopeDataCheck": false, + "checkInvalidKey": true, + "checkSiteMap": true, + "uploadWithSourceMap": true, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "enhance": true + }, + "appid": "wx394efa031612f9b5", + "projectname": "qcloud_device_linkage_demo", + "libVersion": "2.7.7", + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "cloudfunctionTemplateRoot": "cloudfunctionTemplate", + "compileType": "miniprogram", + "condition": { + "search": { + "current": -1, + "list": [] + }, + "conversation": { + "current": -1, + "list": [] + }, + "plugin": { + "current": -1, + "list": [] + }, + "game": { + "list": [] + }, + "miniprogram": { + "current": 0 + } + } +} \ No newline at end of file