├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .start.sh.swp ├── LICENSE ├── README-zh.md ├── README.md ├── _config.yml ├── install.sh ├── install ├── gloablinstall.sh └── nvminstall.sh ├── nohup.out ├── out ├── ConcatParser.html ├── Configuration.html ├── DltDriver.html ├── concatparser.js.html ├── configuration.js.html ├── dltdriver.js.html ├── dltparser.js.html ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.svg │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.svg │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.svg │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.svg │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.svg │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.svg │ └── OpenSans-Regular-webfont.woff ├── global.html ├── index.html ├── logger.html ├── logger.js.html ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js ├── styles │ ├── jsdoc-default.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── wsParser%20-%20function%20to%20parse%20Ali%20Iot%20Linkedge%20websocket%20protocal.html └── wsParser.js.html ├── package.json ├── src ├── concatparser.js ├── config.json ├── configuration.js ├── dltdriver.js ├── dltparser.js ├── index.js ├── logger.js ├── model.json ├── server.json └── wsParser.js ├── start.sh └── test ├── concatparser_test.js ├── configuration_test.js ├── dltdriver_test.js ├── dltparser_test.js ├── testconfig.json ├── testmodel.json └── wsparser_test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | # 注释,忽略文件 2 | node_modules 3 | #**/.js 4 | build 5 | #VS code 6 | .vscode 7 | #git 8 | .gitignore 9 | #nyc 10 | .nyc_output 11 | #test files 12 | test 13 | #jsdoc output 14 | out 15 | #log files 16 | log 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "commonjs": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "globals": { 9 | "Atomics": "readonly", 10 | "SharedArrayBuffer": "readonly" 11 | }, 12 | "parserOptions": { 13 | "ecmaVersion": 2018 14 | }, 15 | "rules": { 16 | "strict": 2,//使用严格模式 17 | "no-alert": 0,//禁止使用alert confirm prompt 18 | "no-array-constructor": 2,//禁止使用数组构造器 19 | "no-bitwise": 0,//禁止使用按位运算符 20 | "no-caller": 1,//禁止使用arguments.caller或arguments.callee 21 | "no-catch-shadow": 2,//禁止catch子句参数与外部作用域变量同名 22 | "no-class-assign": 2,//禁止给类赋值 23 | "no-cond-assign": 2,//禁止在条件表达式中使用赋值语句 24 | "no-const-assign": 2,//禁止修改const声明的变量 25 | "no-constant-condition": 2,//禁止在条件中使用常量表达式 if(true) if(1) 26 | "no-continue": 0,//禁止使用continue 27 | "no-control-regex": 2,//禁止在正则表达式中使用控制字符 28 | "no-debugger": 2,//禁止使用debugger 29 | "no-delete-var": 2,//不能对var声明的变量使用delete操作符 30 | "no-div-regex": 1,//不能使用看起来像除法的正则表达式/=foo/ 31 | "no-dupe-keys": 2,//在创建对象字面量时不允许键重复 {a:1,a:1} 32 | "no-dupe-args": 2,//函数参数不能重复 33 | "no-duplicate-case": 2,//switch中的case标签不能重复 34 | "no-else-return": 2,//如果if语句里面有return,后面不能跟else语句 35 | "no-empty": 2,//块语句中的内容不能为空 36 | "no-empty-character-class": 2,//正则表达式中的[]内容不能为空 37 | "no-labels": 2,//禁止使用空label 38 | "no-eq-null": 2,//禁止对null使用==或!=运算符 39 | "no-eval": 1,//禁止使用eval 40 | "no-ex-assign": 2,//禁止给catch语句中的异常参数赋值 41 | "no-extend-native": 2,//禁止扩展native对象 42 | "no-extra-bind": 2,//禁止不必要的函数绑定 43 | "no-extra-boolean-cast": 2,//禁止不必要的bool转换 44 | "no-extra-parens": 2,//禁止非必要的括号 45 | "no-extra-semi": 2,//禁止多余的冒号 46 | "no-fallthrough": 1,//禁止switch穿透 47 | "no-floating-decimal": 2,//禁止省略浮点数中的0 .5 3. 48 | "no-func-assign": 2,//禁止重复的函数声明 49 | "no-implicit-coercion": 1,//禁止隐式转换 50 | "no-implied-eval": 2,//禁止使用隐式eval 51 | "no-inline-comments": 0,//禁止行内备注 52 | "no-inner-declarations": [2, "functions"],//禁止在块语句中使用声明(变量或函数) 53 | "no-invalid-regexp": 2,//禁止无效的正则表达式 54 | "no-invalid-this": 2,//禁止无效的this,只能用在构造器,类,对象字面量 55 | "no-irregular-whitespace": 2,//不能有不规则的空格 56 | "no-iterator": 2,//禁止使用__iterator__ 属性 57 | "no-label-var": 2,//label名不能与var声明的变量名相同 58 | "no-labels": 2,//禁止标签声明 59 | "no-lone-blocks": 2,//禁止不必要的嵌套块 60 | "no-lonely-if": 2,//禁止else语句内只有if语句 61 | "no-loop-func": 1,//禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以) 62 | "no-mixed-requires": [0, false],//声明时不能混用声明类型 63 | "no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格 64 | "linebreak-style": [0, "unix"],//换行风格 65 | "no-multi-spaces": 1,//不能用多余的空格 66 | "no-multi-str": 2,//字符串不能用\换行 67 | "no-multiple-empty-lines": [1, { "max": 2 }],//空行最多不能超过2行 68 | "no-native-reassign": 2,//不能重写native对象 69 | "no-negated-in-lhs": 2,//in 操作符的左边不能有! 70 | "no-nested-ternary": 0,//禁止使用嵌套的三目运算 71 | "no-new": 1,//禁止在使用new构造一个实例后不赋值 72 | "no-new-func": 1,//禁止使用new Function 73 | "no-new-object": 2,//禁止使用new Object() 74 | "no-new-require": 2,//禁止使用new require 75 | "no-new-wrappers": 2,//禁止使用new创建包装实例,new String new Boolean new Number 76 | "no-obj-calls": 2,//不能调用内置的全局对象,比如Math() JSON() 77 | "no-octal": 2,//禁止使用八进制数字 78 | "no-octal-escape": 2,//禁止使用八进制转义序列 79 | "no-param-reassign": 2,//禁止给参数重新赋值 80 | "no-path-concat": 0,//node中不能使用__dirname或__filename做路径拼接 81 | "no-plusplus": 0,//禁止使用++,-- 82 | "no-process-env": 0,//禁止使用process.env 83 | "no-process-exit": 0,//禁止使用process.exit() 84 | "no-proto": 2,//禁止使用__proto__属性 85 | "no-redeclare": 2,//禁止重复声明变量 86 | "no-regex-spaces": 2,//禁止在正则表达式字面量中使用多个空格 /foo bar/ 87 | "no-restricted-modules": 0,//如果禁用了指定模块,使用就会报错 88 | "no-return-assign": 1,//return 语句中不能有赋值表达式 89 | "no-script-url": 0,//禁止使用javascript:void(0) 90 | "no-self-compare": 2,//不能比较自身 91 | "no-sequences": 0,//禁止使用逗号运算符 92 | "no-shadow": 2,//外部作用域中的变量不能与它所包含的作用域中的变量或参数同名 93 | "no-shadow-restricted-names": 2,//严格模式中规定的限制标识符不能作为声明时的变量名使用 94 | "no-spaced-func": 2,//函数调用时 函数名与()之间不能有空格 95 | "no-sparse-arrays": 2,//禁止稀疏数组, [1,,2] 96 | "no-sync": 0,//nodejs 禁止同步方法 97 | "no-ternary": 0,//禁止使用三目运算符 98 | "no-trailing-spaces": 1,//一行结束后面不要有空格 99 | "no-this-before-super": 0,//在调用super()之前不能使用this或super 100 | "no-throw-literal": 2,//禁止抛出字面量错误 throw "error"; 101 | "no-undef": 1,//不能有未定义的变量 102 | "no-undef-init": 2,//变量初始化时不能直接给它赋值为undefined 103 | "no-undefined": 2,//不能使用undefined 104 | "no-unexpected-multiline": 2,//避免多行表达式 105 | "no-underscore-dangle": [1,{"allowAfterThis":true,"allowAfterSuper":true}],//标识符不能以_开头或结尾 106 | "no-unneeded-ternary": 2,//禁止不必要的嵌套 var isYes = answer === 1 ? true : false; 107 | "no-unreachable": 2,//不能有无法执行的代码 108 | "no-unused-expressions": 2,//禁止无用的表达式 109 | "no-unused-vars": [1, { "vars": "all", "args": "after-used" }],//不能有声明后未被使用的变量或参数 110 | "no-use-before-define": 2,//未定义前不能使用 111 | "no-useless-call": 2,//禁止不必要的call和apply 112 | "no-void": 2,//禁用void操作符 113 | "no-var": 0,//禁用var,用let和const代替 114 | "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告备注 115 | "no-with": 2,//禁用with 116 | "array-bracket-spacing": [2, "never"],//是否允许非空数组里面有多余的空格 117 | "arrow-parens": 0,//箭头函数用小括号括起来 118 | "arrow-spacing": 0,//=>的前/后括号 119 | "accessor-pairs": 0,//在对象中使用getter/setter 120 | "block-scoped-var": 0,//块语句中使用var 121 | "brace-style": [1, "1tbs"],//大括号风格 122 | "callback-return": 1,//避免多次调用回调什么的 123 | "camelcase": 2,//强制驼峰法命名 124 | "comma-dangle": [2, "never"],//对象字面量项尾不能有逗号 125 | "comma-spacing": 0,//逗号前后的空格 126 | "comma-style": [2, "last"],//逗号风格,换行时在行首还是行尾 127 | "complexity": [0, 11],//循环复杂度 128 | "computed-property-spacing": [0, "never"],//是否允许计算后的键名什么的 129 | "consistent-return": 0,//return 后面是否允许省略 130 | "consistent-this": [2, "that"],//this别名 131 | "constructor-super": 0,//非派生类不能调用super,派生类必须调用super 132 | "curly": [2, "all"],//必须使用 if(){} 中的{} 133 | "default-case": 2,//switch语句最后必须有default 134 | "dot-location": 0,//对象访问符的位置,换行的时候在行首还是行尾 135 | "dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括号 136 | "eol-last": 0,//文件以单一的换行符结束 137 | "eqeqeq": 2,//必须使用全等 138 | "func-names": 0,//函数表达式必须有名字 139 | "func-style": [0, "declaration"],//函数风格,规定只能使用函数声明/函数表达式 140 | "generator-star-spacing": 0,//生成器函数*的前后空格 141 | "guard-for-in": 0,//for in循环要用if语句过滤 142 | "handle-callback-err": 0,//nodejs 处理错误 143 | "id-length": 0,//变量名长度 144 | "indent": [2, 4],//缩进风格 145 | "init-declarations": 0,//声明时必须赋初值 146 | "key-spacing": [0, { "beforeColon": false, "afterColon": true }],//对象字面量中冒号的前后空格 147 | "lines-around-comment": 0,//行前/行后备注 148 | "max-depth": [0, 4],//嵌套块深度 149 | "max-len": [0, 80, 4],//字符串最大长度 150 | "max-nested-callbacks": [0, 2],//回调嵌套深度 151 | "max-params": [0, 3],//函数最多只能有3个参数 152 | "max-statements": [0, 10],//函数内最多有几个声明 153 | "new-cap": 2,//函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用 154 | "new-parens": 2,//new时必须加小括号 155 | "newline-after-var": 2,//变量声明后是否需要空一行 156 | "object-curly-spacing": [0, "never"],//大括号内是否允许不必要的空格 157 | "object-shorthand": 0,//强制对象字面量缩写语法 158 | "one-var": 1,//连续声明 159 | "operator-assignment": [0, "always"],//赋值运算符 += -=什么的 160 | "operator-linebreak": [2, "after"],//换行时运算符在行尾还是行首 161 | "padded-blocks": 0,//块语句内行首行尾是否要空行 162 | "prefer-const": 0,//首选const 163 | "prefer-spread": 0,//首选展开运算 164 | "prefer-reflect": 0,//首选Reflect的方法 165 | "quotes": [1, "single"],//引号类型 `` "" '' 166 | "quote-props": [2, "always"],//对象字面量中的属性名是否强制双引号 167 | "radix": 2,//parseInt必须指定第二个参数 168 | "id-match": 0,//命名检测 169 | "require-yield": 0,//生成器函数必须有yield 170 | "semi": [2, "always"],//语句强制分号结尾 171 | "semi-spacing": [0, { "before": false, "after": true }],//分号前后空格 172 | "sort-vars": 0,//变量声明时排序 173 | "space-after-keywords": [0, "always"],//关键字后面是否要空一格 174 | "space-before-blocks": [0, "always"],//不以新行开始的块{前面要不要有空格 175 | "space-before-function-paren": [0, "always"],//函数定义时括号前面要不要有空格 176 | "space-in-parens": [0, "never"],//小括号里面要不要有空格 177 | "space-infix-ops": 0,//中缀操作符周围要不要有空格 178 | "keyword-spacing": 2,//return throw case后面要不要加空格 179 | "space-unary-ops": [0, { "words": true, "nonwords": false }],//一元运算符的前/后要不要加空格 180 | "spaced-comment": 0,//注释风格要不要有空格什么的 181 | "use-isnan": 2,//禁止比较时使用NaN,只能用isNaN() 182 | "valid-jsdoc": 0,//jsdoc规则 183 | "valid-typeof": 2,//必须使用合法的typeof的值 184 | "vars-on-top": 2,//var必须放在作用域顶部 185 | "wrap-iife": [2, "inside"],//立即执行函数表达式的小括号风格 186 | "wrap-regex": 0,//正则表达式字面量用小括号包起来 187 | "yoda": [2, "never"],//禁止尤达条件 188 | "no-console": 2//禁止使用console 189 | 190 | } 191 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | #vscode 15 | .vscode 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | 66 | #log files 67 | -------------------------------------------------------------------------------- /.start.sh.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/.start.sh.swp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 weizy@126.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | [English](README.md)|[中文](README-zh.md) 2 | 3 | --- 4 | # Dlt645驱动说明 5 | 6 | 本项目是阿里云物联网边缘计算网关驱动,用于中国电力标准DLT645智能电表. 目前驱动仅支持Aliyun 物联网平台,符合阿里云边缘网关WebSocket协议。 7 | 可以通过串口连接并采集多个Dlt电表数据(同一串口下,暂不支持多串口)并转换为阿里云边缘网关WebSocket协议。使用前应先连接好Dlt电表与串口。(本地 8 | 调试工具开发中)。 9 | 10 | 11 | ## 阿里云物联网平台操作指南 12 | 13 | 边缘计算网关详细的WebSocket协议见阿里云官网SDK [linkedge-thing-access-websocket_client_sdk](https://github.com/aliyun/linkedge-thing-access-websocket_client_sdk/blob/master/protocol-design-description.md?spm=a2c4g.11186623.2.11.7afd760b9SsoxB&file=protocol-design-description.md) 。 14 | 15 | 目前仅支持 "onlineDevice","reportProperty" 和 "getProperty" 方法,更多方法待开发。 16 | 17 | 使用前需要在[阿里云物联网平台](https://help.aliyun.com/document_detail/73705.html?spm=a2c4g.11174283.2.11.3a8b1668L08yIP)新建产品和设备.新建产品参考模型见项目文件 [model.json](src/model.json). 18 | 19 | 需要安装[阿里云边缘计算网关程序(https://help.aliyun.com/document_detail/102729.html?spm=a2c4g.11186623.6.560.18ab760b5ENITn), 并[部署WebSocket驱动](https://help.aliyun.com/document_detail/122583.html?spm=a2c4g.11186623.6.574.119c71b8bCk3oA).驱动目前仅在Unbuntu 18.04 平台下测试. 20 | 21 | ## 使用指南 22 | 23 | 1. 在[config.json](src/config.json), 文件内设置串口及dlt电表对应参数. 如果网关环境为linux **portName** 部分只需要填写 ttyS1 或者ttyUSB1 ,驱动可以自动解析为 /dev/ttyS1, windows下直接写COM1,COM2 (未测试验证). 24 | 25 | 2. 如果修改了阿里云边缘网关驱动默认参数 (ip地址或者端口), 在 [server.json](src/server.json) 文件修改相应内容。 26 | 27 | 3. 如果要增加更多的Dlt645电表参数(协议支持的), 更新 [model.json](src/model.json) 文件,并且修改[dltparser](src/dltparser.js) 参数以确保变量正确解析。目前还不支持块数据读写。 28 | 29 | 4. **自动安装**下载install.sh脚本并运行,或者复制文件内容到本地新建安装脚本。如果要全局安装node(不推荐),可以注释掉安装脚本里的nvm安装部分以全局安装node。**注意**,cnpm工具仅适用国内环境,中国区外可以注释掉cnpm安装脚本,直接用npm安装即可。 30 | 31 | ## 开发 32 | 33 | 使用Nodejs V8.16.2 34 | 35 | 欢迎Pull Request来修改和增加更多功能。 36 | 37 | [开发文档](out/index.html) 包含了更多细节内容。 38 | 39 | **待办事项** 40 | - 更多DLT645属性参数支持 41 | - 块读取功能支持 42 | - 写参数、修改变量功能支持 43 | - 阿里云边缘网关自动上报事件功能支持 44 | - 阿里云边缘网关服务功能支持 45 | - 虚拟串口测试程序 46 | - WebSocket协议测试程序 47 | - 本地Dlt驱动调试助手 48 | - 本地网页化服务界面用于驱动调试及监控 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English](README.md)|[中文](README-zh.md) 2 | 3 | ---- 4 | 5 | # dlt645Driver 6 | 7 | This is a dlt645 Chinese intelligent power meter driver for Aliyun Iot Edge Gateway driver using WebSocket protocal. The driver is currently works well on Aliyun Iot Platform, you need to setup Aliyun Enviroment, connect Dlt meter on the serialport of linkedge gateway before using it. 8 | 9 | ## Operation Guide on Aliyun Iot Platform 10 | 11 | See Aliyun [linkedge-thing-access-websocket_client_sdk](https://github.com/aliyun/linkedge-thing-access-websocket_client_sdk/blob/master/protocol-design-description.md?spm=a2c4g.11186623.2.11.7afd760b9SsoxB&file=protocol-design-description.md) for the protocal details to connect device with Aliyun Linkedge Gateway. 12 | 13 | Currently the Driver only support "onlineDevice","reportProperty" and "getProperty" methods, the rest methods are to be updated. 14 | 15 | First of all,you need to create a DLT product and devices on [aliyun Iot platform](https://help.aliyun.com/document_detail/73705.html?spm=a2c4g.11174283.2.11.3a8b1668L08yIP).See [model.json](src/model.json) for detailed product properties. 16 | 17 | To use this driver you need to install an Aliyun Linkedge Gate way following [Official Instruction](https://help.aliyun.com/document_detail/102729.html?spm=a2c4g.11186623.6.560.18ab760b5ENITn), and [deploy a WebSocket driver](https://help.aliyun.com/document_detail/122583.html?spm=a2c4g.11186623.6.574.119c71b8bCk3oA).Currently the driver is tested only under Unbuntu 18.04 platform. 18 | 19 | ## To Use 20 | 21 | 1. In [config.json](src/config.json), setup the serialport information as well as the dlt meters. If you are using linux **portName** field just fill like ttyS1 or ttyUSB1 the driver will parse it to /dev/ttyS1, otherwise in windows just use COM1,COM2 (not testified). 22 | 23 | 2. If you have changed default aliyun WebSocket server (ip address and port number), update [server.json](src/server.json) file. Currently TLS support is not available. 24 | 25 | 3. If you want to support more DLT 645 properties, update the [model.json](src/model.json) file, please be sure to modify the propertyCode in [dltparser](src/dltparser.js) too so as the driver can parse relative property properly. 26 | 27 | 4. For aumomated installation download install.sh file first or copy the contents, run install.sh script to finish project installation, if you want to install node global, uncomment the global install file and comment nvminstallation file. If you are out of China, please comment the cnpm installation line and use npm directly. 28 | 29 | 5. Use start.sh script for automated start. 30 | 31 | ## To develop 32 | 33 | Use Nodejs V8.16.2 34 | 35 | Pull request is welcome to add more features. 36 | 37 | See [docs](out/index.html) for development information. 38 | 39 | **Todo List** 40 | - More Dlt645 properties support 41 | - Dlt645 Block property support 42 | - Write property and modify dlt parameter function support 43 | - Aliyun Linkedge setProperty,reportEvent function support 44 | - Aliyun Linkedge callService function support 45 | - Mock serialport function for test 46 | - Mock WebSocket server for test 47 | - Local Dlt 645 debugger 48 | - Local Web debug interface 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-time-machine -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo -e '\033[32m Start to install necessary dependencies \032[0m' 3 | echo -e '\033[37m \037[0m' 4 | sudo apt-get install wget -y 5 | sudo apt-get install curl -y 6 | sudo apt-get install git -y 7 | echo -e '\033[32m Start to download Repository \032[0m' 8 | echo -e '\033[37m \037[0m' 9 | git clone https://github.com/weizy0219/dlt645Driver.git 10 | cd dlt645Driver 11 | cd install 12 | echo -e '\033[32m Start to install project dependencies \032[0m' 13 | echo -e '\033[37m \037[0m' 14 | sudo chmod 700 nvminstall.sh 15 | ./nvminstall 16 | # sudo chmod 700 globalinstall.sh 17 | # ./globalinstall -------------------------------------------------------------------------------- /install/gloablinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo 'start to check node env:' 3 | if ! type node 2>/dev/null || [[ `node -v` != 'v8.17.0' ]] ; then 4 | echo 'node Running Enviroment Error, start to build...' 5 | wget -qO- https://deb.nodesource.com/setup_10.x | sudo -E bash - 6 | sudo apt-get install -y nodejs 7 | echo 'Node install success full' 8 | echo 'Install production package support' 9 | npm install -g cnpm --registry=https://registry.npm.taobao.org 10 | cnpm install --production 11 | echo 'success' 12 | fi 13 | echo 'end' -------------------------------------------------------------------------------- /install/nvminstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo 'start to check node env:' 3 | if ! type node 2>/dev/null || [[ `node -v` != 'v8.16.2' ]] ; then 4 | echo 'node Running Enviroment Error, start to build...' 5 | curl -Ls -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash 6 | export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" 7 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 8 | source ~/.bashrc 9 | nvm install v8.16.2 10 | nvm alias default v8.16.2 11 | echo 'Node install success full' 12 | echo 'Install production package support' 13 | npm install -g cnpm --registry=https://registry.npm.taobao.org 14 | cnpm install --production 15 | echo 'success' 16 | fi 17 | echo 'end' -------------------------------------------------------------------------------- /nohup.out: -------------------------------------------------------------------------------- 1 | [2019-12-21T15:50:42.985] [FATAL] fatal - System Error: Error: The module '/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/build/Release/bindings.node' 2 | was compiled against a different Node.js version using 3 | NODE_MODULE_VERSION 57. This version of Node.js requires 4 | NODE_MODULE_VERSION 79. Please try re-compiling or re-installing 5 | the module (for instance, using `npm rebuild` or `npm install`). 6 | at Object.Module._extensions..node (internal/modules/cjs/loader.js:1190:18) 7 | at Module.load (internal/modules/cjs/loader.js:976:32) 8 | at Function.Module._load (internal/modules/cjs/loader.js:884:14) 9 | at Module.require (internal/modules/cjs/loader.js:1016:19) 10 | at require (internal/modules/cjs/helpers.js:69:18) 11 | at bindings (/home/wdsolar/Projects/dlt645Driver/node_modules/_bindings@1.5.0@bindings/bindings.js:112:48) 12 | at Object. (/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/lib/linux.js:2:36) 13 | at Module._compile (internal/modules/cjs/loader.js:1121:30) 14 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:1160:10) 15 | at Module.load (internal/modules/cjs/loader.js:976:32) 16 | [2019-12-21T15:54:21.077] [FATAL] fatal - System Error: Error: The module '/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/build/Release/bindings.node' 17 | was compiled against a different Node.js version using 18 | NODE_MODULE_VERSION 57. This version of Node.js requires 19 | NODE_MODULE_VERSION 79. Please try re-compiling or re-installing 20 | the module (for instance, using `npm rebuild` or `npm install`). 21 | at Object.Module._extensions..node (internal/modules/cjs/loader.js:1190:18) 22 | at Module.load (internal/modules/cjs/loader.js:976:32) 23 | at Function.Module._load (internal/modules/cjs/loader.js:884:14) 24 | at Module.require (internal/modules/cjs/loader.js:1016:19) 25 | at require (internal/modules/cjs/helpers.js:69:18) 26 | at bindings (/home/wdsolar/Projects/dlt645Driver/node_modules/_bindings@1.5.0@bindings/bindings.js:112:48) 27 | at Object. (/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/lib/linux.js:2:36) 28 | at Module._compile (internal/modules/cjs/loader.js:1121:30) 29 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:1160:10) 30 | at Module.load (internal/modules/cjs/loader.js:976:32) 31 | nohup: failed to run command 'nvm': No such file or directory 32 | [2019-12-21T15:56:42.787] [FATAL] fatal - System Error: Error: The module '/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/build/Release/bindings.node' 33 | was compiled against a different Node.js version using 34 | NODE_MODULE_VERSION 57. This version of Node.js requires 35 | NODE_MODULE_VERSION 79. Please try re-compiling or re-installing 36 | the module (for instance, using `npm rebuild` or `npm install`). 37 | at Object.Module._extensions..node (internal/modules/cjs/loader.js:1190:18) 38 | at Module.load (internal/modules/cjs/loader.js:976:32) 39 | at Function.Module._load (internal/modules/cjs/loader.js:884:14) 40 | at Module.require (internal/modules/cjs/loader.js:1016:19) 41 | at require (internal/modules/cjs/helpers.js:69:18) 42 | at bindings (/home/wdsolar/Projects/dlt645Driver/node_modules/_bindings@1.5.0@bindings/bindings.js:112:48) 43 | at Object. (/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/lib/linux.js:2:36) 44 | at Module._compile (internal/modules/cjs/loader.js:1121:30) 45 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:1160:10) 46 | at Module.load (internal/modules/cjs/loader.js:976:32) 47 | nohup: failed to run command 'nvm': No such file or directory 48 | ./start.sh: line 2: nvm: command not found 49 | [2019-12-21T15:58:56.979] [ERROR] debug - port open error: Error: Error Resource temporarily unavailable Cannot lock port 50 | [2019-12-21T16:00:32.093] [ERROR] debug - port open error: Error: Error Resource temporarily unavailable Cannot lock port 51 | [2019-12-21T18:21:29.764] [FATAL] fatal - System Error: Error: The module '/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/build/Release/bindings.node' 52 | was compiled against a different Node.js version using 53 | NODE_MODULE_VERSION 57. This version of Node.js requires 54 | NODE_MODULE_VERSION 79. Please try re-compiling or re-installing 55 | the module (for instance, using `npm rebuild` or `npm install`). 56 | at Object.Module._extensions..node (internal/modules/cjs/loader.js:1190:18) 57 | at Module.load (internal/modules/cjs/loader.js:976:32) 58 | at Function.Module._load (internal/modules/cjs/loader.js:884:14) 59 | at Module.require (internal/modules/cjs/loader.js:1016:19) 60 | at require (internal/modules/cjs/helpers.js:69:18) 61 | at bindings (/home/wdsolar/Projects/dlt645Driver/node_modules/_bindings@1.5.0@bindings/bindings.js:112:48) 62 | at Object. (/home/wdsolar/Projects/dlt645Driver/node_modules/_@serialport_bindings@8.0.4@@serialport/bindings/lib/linux.js:2:36) 63 | at Module._compile (internal/modules/cjs/loader.js:1121:30) 64 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:1160:10) 65 | at Module.load (internal/modules/cjs/loader.js:976:32) 66 | -------------------------------------------------------------------------------- /out/ConcatParser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: ConcatParser 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: ConcatParser

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

ConcatParser(options-)

32 | 33 |
ConcatParser - class to concat buffer to complete command
34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 |

Constructor

45 | 46 | 47 | 48 |

new ConcatParser(options-)

49 | 50 | 51 | 52 |

To use the `ConcatParser` parser, provide a boundary as a string, buffer, or array of bytes. Runs in O(n) time.

53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
Parameters:
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
NameTypeDescription
options- 94 | 95 | 96 | Object 97 | 98 | 99 | 100 | see test files for more option
112 | 113 | 114 | 115 | 116 | 117 | 118 |
119 | 120 | 121 |
Version:
122 |
  • 1.0.0
123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |
Source:
149 |
152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |
Example
180 | 181 |
const SerialPort = require('serialport');
182 | const ConcatParser = require('./ConcatParser');
183 | const port = new SerialPort('/dev/tty-usbserial1');
184 | const parser = port.pipe(new ConcatParser({ boundary: '\n'));
185 | parser.on('data', (data)=>{console.log});
186 | 187 | 188 | 189 | 190 |
191 | 192 | 193 |

Extends

194 | 195 | 196 | 197 | 198 |
    199 |
  • Transform
  • 200 |
201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 |
223 | 224 |
225 | 226 | 227 | 228 | 229 |
230 | 231 | 234 | 235 |
236 | 237 |
238 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 239 |
240 | 241 | 242 | 243 | 244 | -------------------------------------------------------------------------------- /out/DltDriver.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: DltDriver 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: DltDriver

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

DltDriver(Configuration)

32 | 33 |
DltDriver - class to fetch value from serialPort and update configuration value
34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 |

Constructor

45 | 46 | 47 | 48 |

new DltDriver(Configuration)

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
Parameters:
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
NameTypeDescription
Configuration 92 | 93 | 94 | Object 95 | 96 | 97 | 98 | -
110 | 111 | 112 | 113 | 114 | 115 | 116 |
117 | 118 | 119 |
Version:
120 |
  • 1.0.0
121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
Source:
147 |
150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 |
180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 |
201 | 202 |
203 | 204 | 205 | 206 | 207 |
208 | 209 | 212 | 213 |
214 | 215 |
216 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 217 |
218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /out/concatparser.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: concatparser.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: concatparser.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
'use strict';
 30 | const { Transform } = require('stream');
 31 | const logger=require('./logger'),
 32 |     errorLogger=logger.getLogger('error');
 33 | /**
 34 |  * A transform stream that  concat several data with a specified concat boundary and emits it
 35 |  * The code of this class is based on  DelimiterParser and InterByteTimeout Classes from  serialport package with slight modification
 36 |  * 1. The DelimiterParser is used to split a long data stream to several parts with delimiter,
 37 |  *     while ConcatParser is to concat short data to a longer one
 38 |  * 2. Use includeBoundary :boolean option to decide whether to  include the boundary symbol in data
 39 |  * 3. Use maxBufferSize option to control Buffersize and flush when buffer is full
 40 |  * 4. Use Interval option to flush if timeout
 41 |  * @class ConcatParser - class to concat buffer to complete command
 42 |  * @constructor
 43 |  * @param {Object} options- see test files for more option
 44 |  * @extends Transform
 45 |  * @summary To use the `ConcatParser` parser, provide a boundary as a string, buffer, or array of bytes. Runs in O(n) time.
 46 |  * @example
 47 | const SerialPort = require('serialport');
 48 | const ConcatParser = require('./ConcatParser');
 49 | const port = new SerialPort('/dev/tty-usbserial1');
 50 | const parser = port.pipe(new ConcatParser({ boundary: '\n'));
 51 | parser.on('data', (data)=>{console.log});
 52 |  * @auther weizy@126.com
 53 |  * @version 1.0.0
 54 |  */
 55 | 
 56 | 
 57 | class ConcatParser extends Transform {
 58 |     constructor(options = {}) {
 59 |         super(options);
 60 |         try {
 61 |             if (typeof options.boundary === 'undefined') {
 62 |                 throw new TypeError('"boundary" is not a bufferable object');
 63 |             }
 64 | 
 65 |             if (options.boundary.length === 0) {
 66 |                 throw new TypeError('"boundary" has a 0 or undefined length');
 67 |             }
 68 | 
 69 |             this.includeBoundary = typeof options.includeBoundary !== 'undefined' ? options.includeBoundary : true;
 70 |             this.interval = typeof options.interval !== 'undefined' ? options.interval : 3000;
 71 |             this.maxBufferSize = typeof options.maxBufferSize !== 'undefined' ? options.maxBufferSize : 65535;
 72 |             this.intervalID = -1;
 73 |             this.boundary = Buffer.from(options.boundary);
 74 |             this.buffer = Buffer.alloc(0);
 75 | 
 76 |         } catch (error) {
 77 |             errorLogger.error('Init concatparser error:',error);
 78 |         }
 79 |     }
 80 | 
 81 |     _transform(chunk, encoding, cb) {
 82 | 
 83 |         clearTimeout(this.intervalID);
 84 |         let data = Buffer.concat([this.buffer, chunk]),
 85 |             dataLength = data.length,
 86 |             position;
 87 | 
 88 |         if (dataLength >= this.maxBufferSize) {
 89 |             this.buffer = data.slice(0, this.maxBufferSize);
 90 |             data = Buffer.alloc(0);
 91 |             this.emitData();
 92 |         } else if ((position = data.indexOf(this.boundary)) !== -1) {
 93 |             this.buffer = data.slice(0, position + (this.includeBoundary ? this.boundary.length : 0));
 94 |             data = Buffer.alloc(0);
 95 |             this.emitData();
 96 |         }
 97 |         this.buffer = data;
 98 |         this.intervalID = setTimeout(this.emitData.bind(this), this.interval);
 99 |         cb();
100 |     }
101 | 
102 |     emitData() {
103 |         clearTimeout(this.intervalID);
104 |         if (this.buffer.length > 0) {
105 |             this.push(this.buffer);
106 |         }
107 |         this.buffer = Buffer.alloc(0);
108 |     }
109 |     _flush(cb) {
110 |         this.emitData();
111 |         cb();
112 |     }
113 | }
114 | 
115 | module.exports = ConcatParser;
116 |
117 |
118 | 119 | 120 | 121 | 122 |
123 | 124 | 127 | 128 |
129 | 130 |
131 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 132 |
133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /out/dltdriver.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: dltdriver.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: dltdriver.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
'use strict';
 30 | const SerialPort = require('serialport'),
 31 |     bufToCmd = require('./dltparser').bufToCmd,
 32 |     cmdToBuf = require('./dltparser').cmdToBuf;
 33 | const ConcatParser = require('./concatparser');
 34 | const logger = require('./logger'),
 35 |     errorLogger = logger.getLogger('debug');
 36 | 
 37 | /**
 38 | *@class DltDriver - class to fetch value from serialPort and update configuration value
 39 | *@constructor
 40 | *@param {Object} Configuration -
 41 | *@auther weizy@126.com
 42 | *@version 1.0.0
 43 | */
 44 | class DltDriver {
 45 |     constructor(configuration) {
 46 |         // this._config=JSON.parse(configuration);
 47 |         this._portName = '';
 48 |         this._portSet = {};
 49 |         this._updateInterval = 0;
 50 |         this._initPort(configuration);
 51 |         this._port = new SerialPort(this._portName, this._portSet, (err) => {
 52 |             if (err !== null) {
 53 |                 errorLogger.error('port open error:', err);
 54 |             }
 55 |         });
 56 |         this._concat = new ConcatParser({ 'boundary': [0x16] });
 57 |         this._parser = this._port.pipe(this._concat);
 58 |         this._meterList = configuration.meterList;
 59 |         this._meterListLength = this._meterList.length;
 60 | 
 61 |         //here calls parser to concat data and update relative value in configuration class
 62 |         this._parser.on('data', (data) => {
 63 |             let cmd = bufToCmd(data);
 64 | 
 65 |             if (cmd) {
 66 |                 this._updateValue(cmd, configuration);
 67 |             }
 68 | 
 69 |         });
 70 |         this._yld = this._fetchValue();
 71 |         this._fetchInterval = setInterval(() => {
 72 |             if (this._meterListLength > 0) {
 73 |                 let buf = this._yld.next().value;
 74 | 
 75 |                 if (buf.length > 1) {
 76 |                     this._port.write(buf, (err) => {
 77 |                         if (err) {
 78 |                             errorLogger.error('fetch meter list error: ', err);
 79 |                         }
 80 |                     });
 81 |                 }
 82 | 
 83 |             }
 84 |         }, this._updateInterval);
 85 | 
 86 |         this._initPort.bind(this);
 87 |         this._updateValue.bind(this);
 88 |         this._fetchValue.bind(this);
 89 |         this.closePort.bind(this);
 90 |     }
 91 | 
 92 |     /**
 93 |  * @method _initPort
 94 |  * @private
 95 |  * @summary update port value of dltdriver from configuration file
 96 |  */
 97 |     _initPort(config) {
 98 |         try {
 99 | 
100 |             let driverInfo = JSON.parse(config['driverInfo']);
101 | 
102 |             if (driverInfo) {
103 |                 let rawPortName = driverInfo['portName'];
104 | 
105 |                 this._portName = rawPortName.replace('tty', '/dev/tty');
106 |                 this._updateInterval = driverInfo['updateInterval'];
107 |                 this._portSet['baudRate'] = driverInfo['baudRate'];
108 |                 this._portSet['dataBits'] = driverInfo['dataBits'];
109 |                 this._portSet['parity'] = driverInfo['parity'];
110 |                 this._portSet['stopBits'] = driverInfo['stopBits'];
111 |                 this._portSet['autoOpen'] = driverInfo['autoOpen'];
112 | 
113 |             }
114 |         } catch (error) {
115 |             errorLogger.error('Error to init port in dltDriver:',error);
116 |         }
117 |     }
118 |     /**
119 |  * @method _updateValue - to update meterlist value in configuration
120 |  * @private
121 |  * @param {Object} cmd - a custom formed object,see parser
122 |  * @param {Object} configuration -see configuration file
123 |  * @summary translate from cmd to configuration format and call configuration to update
124 |  */
125 |     _updateValue(cmd, configuration) {
126 |         if (cmd !== {} && cmd['sn'] && cmd['data'] && cmd['data']['propertyName'] !== 'errCode') {
127 | 
128 |             let msg = {},
129 |                 property = {},
130 |                 data = cmd['data'];
131 | 
132 |             msg['meterSn'] = cmd['sn'];
133 |             msg['properties'] = [];
134 |             property['identifier'] = data['propertyName'];
135 |             property['value'] = data['value'];
136 |             msg['properties'].push(property);
137 |             configuration.updateProperty(JSON.stringify(msg));
138 |         }
139 | 
140 |     }
141 |     /**
142 |  * @method *_fetchValue - yield function to feed driver
143 |  * @private
144 |  * @summary yield all properties from meterList and generate command for driver to call port and send
145 |  */
146 |     *_fetchValue() {
147 |         const cmd = {
148 |                 'sn': '0000000000',
149 |                 'cmd': '11',
150 |                 'data': {
151 |                     'status': true,
152 |                     'propertyName': 'elecUa',
153 |                     'value': 0
154 |                 }
155 |             },
156 |             meterList = [];
157 | 
158 |         for (let i = 0; i < this._meterListLength; i++) {
159 |             meterList.push(JSON.parse(this._meterList[i]));
160 |         }
161 |         let index = 0;
162 | 
163 |         while (true) {
164 |             let meter = meterList[index],
165 |                 properties = meter['properties'],
166 |                 propLength = properties.length;
167 | 
168 |             cmd['sn'] = meter['meterSn'];
169 |             cmd['cmd'] = '11';
170 | 
171 | 
172 |             for (let i = 0; i < propLength; i++) {
173 |                 let property = properties[i];
174 | 
175 |                 cmd['data']['propertyName'] = property['identifier'];
176 |                 cmd['data']['status'] = true;
177 |                 cmd['data']['value'] = 0;
178 |                 yield cmdToBuf(cmd);
179 |             }
180 |             index++;
181 |             if (index >= this._meterList.length) {
182 |                 index = 0;
183 |             }
184 |         }
185 | 
186 |     }
187 |     /**
188 |  * @method closePort
189 |  * @public
190 |  * @summary close the port
191 |  */
192 |     closePort(){
193 |         this._port.close();
194 |     }
195 | }
196 | 
197 | module.exports = DltDriver;
198 | 
199 |
200 |
201 | 202 | 203 | 204 | 205 |
206 | 207 | 210 | 211 |
212 | 213 |
214 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 215 |
216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /out/dltparser.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: dltparser.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: dltparser.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
'use strict';
 30 | 
 31 | const logger=require('./logger'),
 32 |     infoLogger=logger.getLogger('info'),
 33 |     // fix buf data format, see dlt 645 for detail
 34 |     preBuf = Buffer.alloc(4).fill('fe', 'hex'),
 35 |     divBuf = Buffer.from([0x68]),
 36 |     endBuf = Buffer.from([0x16]),
 37 | 
 38 |     //fix property Code
 39 |     propertyCode = {
 40 |         'elecFh': '02800002',
 41 |         'elecFr': '02030000',
 42 |         'elecFra': '02030100',
 43 |         'elecFrb': '02030200',
 44 |         'elecFrc': '02030300',
 45 |         'elecPf': '02060000',
 46 |         'elecPq': '02040000',
 47 |         'elecPqa': '02040100',
 48 |         'elecPqb': '02040200',
 49 |         'elecPqc': '02040300',
 50 |         'elecCa': '02020100',
 51 |         'elecCb': '02020200',
 52 |         'elecCc': '02020300',
 53 |         'elecUa': '02010100',
 54 |         'elecUb': '02010200',
 55 |         'elecUc': '02010300',
 56 |         'elecAe': '00000000',
 57 |         'elecAef': '00010000',
 58 |         'elecAea': '00150000',
 59 |         'elecAeb': '00290000',
 60 |         'elecAec': '003d0000',
 61 |         'elecFef': '00020000',
 62 |         'elecFea': '00160000',
 63 |         'elecFeb': '002a0000',
 64 |         'elecFec': '003e0000'
 65 |     };
 66 | 
 67 | 
 68 | /**
 69 |  * @function cmdToBuf - a parser function to transform custom command to Serialport buffer
 70 |  * @param {Object} - cmd, a custom format of command
 71 |  * @returns {Buffer} - A serialport command Buffer
 72 |  *To use the `dltparser` parser, provide a structured cmd or a Buffer of Dlt protocal
 73 |  *Bellow are dataformat request
 74 |     const cmd = {
 75 |         sn: '3411001043',
 76 |         cmd: '11',
 77 |         data: {
 78 |             status:true,
 79 |             propertyName: 'elecUa',
 80 |             value:0
 81 |         }
 82 |     }
 83 | 
 84 |     let encodedData = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 0x34,
 85 |         0x00, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x4d, 0x16]);
 86 |  */
 87 | //First export function to transfer cmd to buf , cmd should be the same as above format
 88 | exports.cmdToBuf = (cmd) => {
 89 |     //meter sn shall not be longer than 12 bytes or will be cutoff
 90 |     //the buffer will be filled with 0 if meter sn is shorter than 12
 91 |     let encodeSn = (sn) => {
 92 |             const snLength = sn.length,
 93 |                 snBuf = Buffer.alloc(6).fill(0, 'hex');
 94 | 
 95 |             for (let i = 0; i < snLength; i++) {
 96 |                 let stopIndex = snLength - 2 * i;
 97 | 
 98 |                 if (stopIndex <= 0) {
 99 |                     break;
100 |                 }
101 |                 snBuf.write(sn.substring(stopIndex - 2, stopIndex), i, 'hex');
102 |             }
103 |             return snBuf;
104 |         },
105 | 
106 |         // Current V1.0 only support encodeData from master to slave
107 |         //with fixed data length
108 |         encodeData = () => {
109 |             const fixBias = Buffer.from([0x33]),
110 |                 cBuf = Buffer.from(cmd.cmd, 'hex'),
111 |                 dataBuf = Buffer.from(propertyCode[cmd.data.propertyName], 'hex'),
112 |                 lBuf = Buffer.from([dataBuf.length], 'hex');
113 | 
114 |             for (let i = 0; i < dataBuf.length; i++) {
115 |                 dataBuf[i] = dataBuf[i] + fixBias[0];
116 |             }
117 |             return Buffer.concat([cBuf, lBuf, dataBuf.reverse()]);
118 |         };
119 | 
120 | 
121 |     try {
122 |         const cmdBuf = Buffer.concat([divBuf, encodeSn(cmd.sn),
123 |             divBuf, encodeData(cmd.sn)]);
124 |         let csBuf = Buffer.from([0x00]);
125 | 
126 |         for (let i = 0; i < cmdBuf.length; i++) {
127 |             csBuf[0] = csBuf[0] + cmdBuf[i];
128 |         }
129 |         return Buffer.concat([preBuf, cmdBuf, csBuf, endBuf]);
130 |     } catch (e) {
131 |         infoLogger.info('parse command to buffer error:',e);
132 |         return Buffer.alloc(0);
133 |     }
134 | 
135 | };
136 | 
137 | /**
138 |  * A  dltparser module which allows to translate between structured cmd and Buffer
139 |  * This version is a sync function ,for async function see promise version
140 |  * currently only support limited command code like 11,91,b1 and d1, further support is under developing
141 |  * TODO  support more Dlt code in the future
142 |  * @function bufToCmd - a parser function to transform Serialport buffer back into custom Cmd
143 |  * @param {Object} - cmd, a custom format of Buffer comply with Dlt645-2007
144 |  * @returns {Object} - A custom serialport command B
145 |  * To use the `dltparser` parser, provide a structured cmd or a Buffer of Dlt protocal
146 |  * dataformat Bellow are dataformat request
147 |     const cmd = {
148 |         sn: '3411001043',
149 |         cmd: '11',
150 |         data: {
151 |             status:true,
152 |             propertyName: 'elecUa',
153 |             value:0
154 |         }
155 |     }
156 | 
157 |     let encodedData = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 0x34,
158 |         0x00, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x4d, 0x16]);
159 | 
160 |  */
161 | exports.bufToCmd = (buf) => {
162 |     //meter sn shall not be longer than 12 bytes or will be cutoff
163 |     //the buffer will be filled with 0 if meter sn is shorter than 12
164 |     let csCheck=(sbuf,cs)=>{
165 |             let ibuf=Buffer.from(sbuf),
166 |                 csBuf = Buffer.from([0x00]);
167 | 
168 |             for (let i = 0; i < ibuf.length; i++) {
169 |                 csBuf[0] = csBuf[0] + ibuf[i];
170 |             }
171 |             return csBuf[0]===cs[0];
172 |         },
173 | 
174 |         decodeSn=(sbuf,start,div)=>{
175 |             let ibuf=Buffer.from(sbuf),
176 |                 snBuf=ibuf.slice(start+1,div),
177 |                 sn=snBuf.reverse().toString('hex');
178 | 
179 |             return sn.substr(sn.search(/[^0]/));
180 |         },
181 | 
182 |         decodeData=(sbuf,div)=>{
183 |             let ibuf=Buffer.from(sbuf),
184 |                 cmd={
185 |                     'cmd':'',
186 |                     'data':{
187 |                         'status': false,
188 |                         'propertyName':'',
189 |                         'value': 0
190 |                     }
191 |                 };
192 | 
193 |             cmd.cmd=ibuf[div+1].toString(16);
194 | 
195 |             if (cmd.cmd==='91'||cmd.cmd==='b1'){
196 |                 let dataLength=ibuf[div+2],
197 |                     divBias=div+3,
198 |                     cmdLength=4,
199 |                     fixBias = Buffer.from([0x33]),
200 |                     propBuf=ibuf.slice(divBias,divBias+cmdLength),
201 |                     dataBuf=ibuf.slice(divBias+cmdLength,divBias+dataLength),
202 |                     dataValue=0,
203 |                     cmdStr='',
204 |                     tem=0;
205 | 
206 |                 for (let i=0;i<propBuf.length;i++){
207 |                     propBuf[i]=propBuf[i]-fixBias[0];
208 |                 }
209 | 
210 |                 for (let i=0;i<dataBuf.length;i++){
211 |                     dataBuf[i]=dataBuf[i]-fixBias[0];
212 |                     tem=parseInt(dataBuf[i].toString(16),10);
213 |                     dataValue=dataValue+tem*100**i;
214 |                 }
215 |                 cmdStr=propBuf.reverse().toString('hex');
216 | 
217 |                 for (let key in propertyCode){
218 |                     if (propertyCode[key]===cmdStr){
219 |                         cmd.data.propertyName=key;
220 |                         cmd.data.value=dataValue;
221 |                         cmd.data.status=true;
222 |                         break;
223 |                     }
224 |                 }
225 |                 return cmd;
226 |             } else if (cmd.cmd==='d1'){
227 |                 cmd.data.value=buf.slice(buf.length-3,buf.length-2).toString('hex');
228 |                 cmd.data.propertyName='errCode';
229 |                 return cmd;
230 |             }
231 |             cmd.data.value='-1';
232 |             cmd.data.propertyName='errCode';
233 |             return cmd;
234 | 
235 |         };
236 | 
237 | 
238 |     try {
239 |         let start = buf.indexOf(divBuf),
240 |             div= buf.lastIndexOf(divBuf),
241 |             end = buf.indexOf(endBuf);
242 | 
243 |         if (csCheck(buf.slice(start,end-1),buf.slice(end-1,end))!==true){
244 |             return {
245 |                 'cmd':'',
246 |                 'data':{
247 |                     'status': false,
248 |                     'propertyName':'errCode',
249 |                     'value': 'csCheck Error'
250 |                 }
251 |             };
252 |         }
253 |         const cmd=decodeData(buf,div);
254 | 
255 |         cmd.sn=decodeSn(buf,start,div);
256 |         return cmd;
257 |     } catch (e) {
258 |         infoLogger.info('parse buffer to command error:',e);
259 |         return {
260 |             'cmd':'',
261 |             'data':{
262 |                 'status': false,
263 |                 'propertyName':'errCode',
264 |                 'value': e.toString()
265 |             }
266 |         };
267 |     }
268 | 
269 | };
270 | 
271 | 
272 |
273 |
274 | 275 | 276 | 277 | 278 |
279 | 280 | 283 | 284 |
285 | 286 |
287 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 288 |
289 | 290 | 291 | 292 | 293 | 294 | -------------------------------------------------------------------------------- /out/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /out/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /out/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johngai19/dlt645Driver/c5f3a16ea0aacdab1b454b62b825a4dff2d00aa4/out/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /out/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Home 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Home

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | 55 | 56 |
57 | 58 |
59 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 60 |
61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /out/logger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: logger 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: logger

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

logger(configFile, modelFile)

32 | 33 |
Configuration - class to create,save and update all global data
34 | 35 | 36 |
37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 |

Constructor

45 | 46 | 47 | 48 |

new logger(configFile, modelFile)

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
Parameters:
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
NameTypeDescription
configFile 92 | 93 | 94 | string 95 | 96 | 97 | 98 | the path and file name of config file
modelFile 115 | 116 | 117 | string 118 | 119 | 120 | 121 | the path and file name of model file
133 | 134 | 135 | 136 | 137 | 138 | 139 |
140 | 141 | 142 |
Version:
143 |
  • 1.0.0
144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 |
Source:
170 |
173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 |
224 | 225 |
226 | 227 | 228 | 229 | 230 |
231 | 232 | 235 | 236 |
237 | 238 |
239 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 240 |
241 | 242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /out/logger.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: logger.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: logger.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
'use strict';
 30 | /**
 31 | *@function getLogger - function to build a log4js module so as to make logging easier
 32 | *@param {string} name - string format of log file
 33 | *@example
 34 |         const logger  = require("./logger");
 35 |         infoLogger  = log4js.getLogger("info");
 36 |         traceLogger  = log4js.getLogger("trace");
 37 |         debugLogger  = log4js.getLogger("debug");
 38 | 
 39 |         infoLogger.info("现在时间:",new Date().toLocaleString());
 40 |         traceLogger.trace("现在时间:",new Date().toLocaleString());
 41 |         debugLogger.debug("现在时间:",new Date().toLocaleString());
 42 | *@auther weizy@126.com
 43 | *@version 1.0.0
 44 | */
 45 | const log4js = require('log4js');
 46 | 
 47 | log4js.configure({
 48 |     'replaceConsole':false,
 49 |     'appenders':{
 50 |         'stdout':{
 51 |             'type':'console'
 52 |         },
 53 |         'trace':{
 54 |             'type':'dateFile',
 55 |             'filename':'log/log/',
 56 |             'pattern':'trace-yyyy-MM-dd.log',
 57 |             'alwaysIncludePattern':true
 58 |         },
 59 |         'debug':{
 60 |             'type':'dateFile',
 61 |             'filename':'log/log/',
 62 |             'pattern':'debug-yyyy-MM-dd.log',
 63 |             'alwaysIncludePattern':true
 64 |         },
 65 |         'info':{
 66 |             'type':'dateFile',
 67 |             'filename':'log/log/',
 68 |             'pattern':'info-yyyy-MM-dd.log',
 69 |             'alwaysIncludePattern':true
 70 |         },
 71 |         'warn':{
 72 |             'type':'dateFile',
 73 |             'filename':'log/log/',
 74 |             'pattern':'warn-yyyy-MM-dd.log',
 75 |             'alwaysIncludePattern':true
 76 |         },
 77 |         'error':{
 78 |             'type':'dateFile',
 79 |             'filename':'log/log/',
 80 |             'pattern':'err-yyyy-MM-dd.log',
 81 |             'alwaysIncludePattern':true
 82 |         },
 83 |         'fatal':{
 84 |             'type':'dateFile',
 85 |             'filename':'log/log/',
 86 |             'pattern':'fatal-yyyy-MM-dd.log',
 87 |             'alwaysIncludePattern':true
 88 |         }
 89 |     },
 90 |     'categories':{
 91 |         'default':{'appenders':['stdout','info'],'level':'info'},
 92 |         'trace':{'appenders':['stdout','trace'],'level':'trace'},
 93 |         'debug':{'appenders':['stdout','debug'],'level':'debug'},
 94 |         'warn':{'appenders':['stdout','warn'],'level':'warn'},
 95 |         'error':{'appenders':['stdout','error'],'level':'error'},
 96 |         'fatal':{'appenders':['stdout','fatal'],'level':'fatal'}
 97 |     }
 98 | });
 99 | 
100 | exports.getLogger=function(name){
101 |     return log4js.getLogger(name||'info');
102 | };
103 | 
104 | /**
105 | *@function useLogger - logger for express project
106 | *@param {expressapp} app - Express format app
107 | *@param {log4js} logger - log4js
108 | *@auther weizy@126.com
109 | *@version 1.0.0
110 | */
111 | exports.useLogger=function(app,logger){
112 |     app.use(log4js.connectLogger(logger||log4js.getLogger('info'),{
113 |         'format': '[:remote-addr :method :url :status :response-timems][:referrer HTTP/:http-version :user-agent]'
114 |     }));
115 | };
116 |
117 |
118 | 119 | 120 | 121 | 122 |
123 | 124 | 127 | 128 |
129 | 130 |
131 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 132 |
133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /out/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (() => { 3 | const source = document.getElementsByClassName('prettyprint source linenums'); 4 | let i = 0; 5 | let lineNumber = 0; 6 | let lineId; 7 | let lines; 8 | let totalLines; 9 | let anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = `line${lineNumber}`; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /out/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /out/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /out/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /out/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /out/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /out/wsParser%20-%20function%20to%20parse%20Ali%20Iot%20Linkedge%20websocket%20protocal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: wsParser - function to parse Ali Iot Linkedge websocket protocal 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: wsParser - function to parse Ali Iot Linkedge websocket protocal

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

wsParser - function to parse Ali Iot Linkedge websocket protocal(msgStr, config, callback)

32 | 33 | 34 |
35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 |

new wsParser - function to parse Ali Iot Linkedge websocket protocal(msgStr, config, callback)

45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
Parameters:
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
NameTypeDescription
msgStr 88 | 89 | 90 | string 91 | 92 | 93 | 94 | JSON string form webSocket message
config 111 | 112 | 113 | Object 114 | 115 | 116 | 117 | configuration class object
callback 134 | 135 | 136 | function 137 | 138 | 139 | 140 | callback function to send message back to server
152 | 153 | 154 | 155 | 156 | 157 | 158 |
159 | 160 | 161 |
Version:
162 |
  • 1.0.0
163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 |
Source:
189 |
192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 |
200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 |
222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 |
243 | 244 |
245 | 246 | 247 | 248 | 249 |
250 | 251 | 254 | 255 |
256 | 257 |
258 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 259 |
260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /out/wsParser.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: wsParser.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: wsParser.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
'use strict';
 30 | /**
 31 | *@function wsParser - function to parse Ali Iot Linkedge websocket protocal
 32 | *@constructor
 33 | *@param {string} msgStr - JSON string form webSocket message
 34 | *@param {Object} config  - configuration class object
 35 | *@param {function} callback - callback function to send message back to server
 36 | *@auther weizy@126.com
 37 | *@version 1.0.0
 38 | */
 39 | 
 40 | module.exports = function (msgStr, config, cb) {
 41 | 
 42 |     let msg = JSON.parse(msgStr),
 43 |         version = msg.version ? msg.version : '1.0',
 44 |         message = msg.message ? msg.message : '',
 45 |         payload = msg.payload ? msg.payload : {},
 46 |         code = msg.code ? msg.code : 0,
 47 |         method = msg.method ? msg.method : '';
 48 | 
 49 |     const callRegDevice = function (regMethod) {
 50 |             let devices = config.onlineDevice(),
 51 |                 devLength = devices.length,
 52 |                 resList = [],
 53 |                 resMsg = {
 54 |                     'version': '1.0',
 55 |                     'method': regMethod,
 56 |                     'messageId': regMethod === 'onlineDevice' ? 1 : 2,
 57 |                     'payload': {}
 58 |                 };
 59 | 
 60 |             for (let i = 0; i < devLength; i++) {
 61 |                 Object.assign(resMsg['payload'], devices[i]);
 62 |                 resList.push(JSON.parse(JSON.stringify(resMsg)));
 63 |             }
 64 |             return resList;
 65 |         },
 66 |         callReportProperty = function () {
 67 |             let devices = config.reportProperty(),
 68 |                 devLength = devices.length,
 69 |                 resList = [],
 70 |                 resMsg = {
 71 |                     'version': '1.0',
 72 |                     'method': 'reportProperty',
 73 |                     'messageId': 0,
 74 |                     'payload': {}
 75 |                 };
 76 | 
 77 |             for (let i = 0; i < devLength; i++) {
 78 |                 //Object.assign(resMsg['payload'], devices[i]);
 79 |                 resMsg['payload'] = devices[i];
 80 |                 resList.push(JSON.parse(JSON.stringify(resMsg)));
 81 |             }
 82 |             return resList;
 83 |         },
 84 |         callGetProperty = function () {
 85 |             let reqMsg = {},
 86 |                 resProperties = [],
 87 |                 resMsg = {};
 88 | 
 89 |             reqMsg['productKey'] = payload['productKey'];
 90 |             reqMsg['deviceName'] = payload['deviceName'];
 91 |             reqMsg['properties'] = payload['properties'];
 92 |             resProperties = config.getProperty(JSON.stringify(reqMsg));
 93 |             resMsg = { 'code': 0, 'messageId': 3, 'payload': { 'properties': resProperties } };
 94 | 
 95 |             return resMsg;
 96 |         },
 97 | 
 98 | 
 99 |         dispatch = function () {
100 |             if (code === 0 && message === 'Success') {
101 |                 let rest = {
102 |                     'code': 100000,
103 |                     'message': 'Unrecognized method.'
104 |                 };
105 | 
106 |                 return cb(rest);
107 |             }
108 | 
109 |             if (version === '1.0') {
110 |                 if (method) {
111 |                     if (method === 'onlineDevice') {
112 |                         let rest = callRegDevice('onlineDevice');
113 | 
114 |                         return cb(rest);
115 | 
116 |                     } else if (method === 'offlineDevice') {
117 |                         let rest = callRegDevice('offlineDevice');
118 | 
119 |                         return cb(rest);
120 | 
121 |                     } else if (method === 'reportProperty') {
122 |                         let rest = callReportProperty();
123 | 
124 |                         return cb(rest);
125 | 
126 |                     } else if (method === 'getProperty') {
127 |                         let rest = callGetProperty();
128 | 
129 |                         return cb(rest);
130 |                     } else if (method === 'setProperty') {
131 |                         let rest = {
132 |                             'code': 109003,
133 |                             'message': 'failure, property readonly.'
134 |                         };
135 | 
136 |                         return cb(rest);
137 | 
138 |                     } else if (method === 'callService') {
139 |                         let rest = {
140 |                             'code': 109005,
141 |                             'message': 'The requested service does not exist.'
142 |                         };
143 | 
144 |                         return cb(rest);
145 |                     }
146 |                     let rest = {
147 |                         'code': 100000,
148 |                         'message': 'Unrecognized method.'
149 |                     };
150 | 
151 |                     return cb(rest);
152 |                 }
153 |                 let rest = {
154 |                     'code': 100000,
155 |                     'message': 'Unrecognized method.'
156 |                 };
157 | 
158 |                 return cb(rest);
159 | 
160 |             }
161 |             let rest = {
162 |                 'code': 100000,
163 |                 'message': 'Unrecognized method.'
164 |             };
165 | 
166 |             return cb(rest);
167 | 
168 |         };
169 | 
170 |     return dispatch();
171 | };
172 |
173 |
174 | 175 | 176 | 177 | 178 |
179 | 180 | 183 | 184 |
185 | 186 |
187 | Documentation generated by JSDoc 3.6.3 on Wed Dec 18 2019 10:43:58 GMT+0800 (CST) 188 |
189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dlt645driver", 3 | "version": "1.0.0", 4 | "description": "this is a dlt 645 intelligent power meter driver for aliyun edge gateway", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "cov": "nyc mocha", 9 | "start": "node src/index.js", 10 | "lint": "eslint .", 11 | "fix": "eslint --fix .", 12 | "doc": "npx jsdoc --private src/" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/weizy0219/dlt645Driver.git" 17 | }, 18 | "keywords": [ 19 | "dlt645", 20 | "aliyun", 21 | "edge", 22 | "gateway", 23 | "driver" 24 | ], 25 | "author": "weizy@126.com", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/weizy0219/dlt645Driver/issues" 29 | }, 30 | "homepage": "https://github.com/weizy0219/dlt645Driver#readme", 31 | "devDependencies": { 32 | "chai": "^4.2.0", 33 | "eslint": "^6.7.2", 34 | "jsdoc": "^3.6.3", 35 | "mocha": "^6.2.2", 36 | "nyc": "^14.1.1", 37 | "pre-commit": "^1.2.2", 38 | "sinon": "^7.5.0" 39 | }, 40 | "pre-commit": [ 41 | "fix", 42 | "lint" 43 | ], 44 | "dependencies": { 45 | "log4js": "^6.1.0", 46 | "serialport": "^8.0.5", 47 | "websocket": "^1.0.31" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/concatparser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { Transform } = require('stream'); 3 | const logger=require('./logger'), 4 | errorLogger=logger.getLogger('error'); 5 | /** 6 | * A transform stream that concat several data with a specified concat boundary and emits it 7 | * The code of this class is based on DelimiterParser and InterByteTimeout Classes from serialport package with slight modification 8 | * 1. The DelimiterParser is used to split a long data stream to several parts with delimiter, 9 | * while ConcatParser is to concat short data to a longer one 10 | * 2. Use includeBoundary :boolean option to decide whether to include the boundary symbol in data 11 | * 3. Use maxBufferSize option to control Buffersize and flush when buffer is full 12 | * 4. Use Interval option to flush if timeout 13 | * @class ConcatParser - class to concat buffer to complete command 14 | * @constructor 15 | * @param {Object} options- see test files for more option 16 | * @extends Transform 17 | * @summary To use the `ConcatParser` parser, provide a boundary as a string, buffer, or array of bytes. Runs in O(n) time. 18 | * @example 19 | const SerialPort = require('serialport'); 20 | const ConcatParser = require('./ConcatParser'); 21 | const port = new SerialPort('/dev/tty-usbserial1'); 22 | const parser = port.pipe(new ConcatParser({ boundary: '\n')); 23 | parser.on('data', (data)=>{console.log}); 24 | * @auther weizy@126.com 25 | * @version 1.0.0 26 | */ 27 | 28 | 29 | class ConcatParser extends Transform { 30 | constructor(options = {}) { 31 | super(options); 32 | try { 33 | if (typeof options.boundary === 'undefined') { 34 | throw new TypeError('"boundary" is not a bufferable object'); 35 | } 36 | 37 | if (options.boundary.length === 0) { 38 | throw new TypeError('"boundary" has a 0 or undefined length'); 39 | } 40 | 41 | this.includeBoundary = typeof options.includeBoundary !== 'undefined' ? options.includeBoundary : true; 42 | this.interval = typeof options.interval !== 'undefined' ? options.interval : 3000; 43 | this.maxBufferSize = typeof options.maxBufferSize !== 'undefined' ? options.maxBufferSize : 65535; 44 | this.intervalID = -1; 45 | this.boundary = Buffer.from(options.boundary); 46 | this.buffer = Buffer.alloc(0); 47 | 48 | } catch (error) { 49 | errorLogger.error('Init concatparser error:',error); 50 | } 51 | } 52 | 53 | _transform(chunk, encoding, cb) { 54 | 55 | clearTimeout(this.intervalID); 56 | let data = Buffer.concat([this.buffer, chunk]), 57 | dataLength = data.length, 58 | position; 59 | 60 | if (dataLength >= this.maxBufferSize) { 61 | this.buffer = data.slice(0, this.maxBufferSize); 62 | data = Buffer.alloc(0); 63 | this.emitData(); 64 | } else if ((position = data.indexOf(this.boundary)) !== -1) { 65 | this.buffer = data.slice(0, position + (this.includeBoundary ? this.boundary.length : 0)); 66 | data = Buffer.alloc(0); 67 | this.emitData(); 68 | } 69 | this.buffer = data; 70 | this.intervalID = setTimeout(this.emitData.bind(this), this.interval); 71 | cb(); 72 | } 73 | 74 | emitData() { 75 | clearTimeout(this.intervalID); 76 | if (this.buffer.length > 0) { 77 | this.push(this.buffer); 78 | } 79 | this.buffer = Buffer.alloc(0); 80 | } 81 | _flush(cb) { 82 | this.emitData(); 83 | cb(); 84 | } 85 | } 86 | 87 | module.exports = ConcatParser; -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "driverInfo": { 3 | "portName":"ttyS4", 4 | "baudRate": 1200, 5 | "dataBits": 8, 6 | "parity": "even", 7 | "stopBits": 1, 8 | "autoOpen":true, 9 | "updateInterval":5000 10 | }, 11 | "meterList": [ 12 | { 13 | "productKey":"a18V18bpG5P", 14 | "deviceName":"VP001_101", 15 | "meterSn":"3411001043" 16 | } 17 | ], 18 | "reportInterval":10000 19 | } 20 | -------------------------------------------------------------------------------- /src/configuration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | 4 | *@class Configuration - class to create,save and update all global data 5 | *@constructor 6 | *@param {string} configFile - the path and file name of config file 7 | *@param {string} modelFile - the path and file name of model file 8 | *@auther weizy@126.com 9 | *@version 1.0.0 10 | */ 11 | const logger=require('./logger'), 12 | errorLogger=logger.getLogger('trace'); 13 | 14 | class Configuration { 15 | 16 | constructor(configFile, modelFile) { 17 | try { 18 | this._config = require(configFile); 19 | this._driverInfo = this._config.driverInfo; 20 | this._reportInterval = this._config.reportInterval; 21 | this._model = require(modelFile); 22 | //this._meterList is an array of String format which need to be parsed to use 23 | this._meterList = this._buildDeviceList(this._config.meterList, this._model.properties); 24 | this._buildDeviceList.bind(this); 25 | this._getAllProperties.bind(this); 26 | this.getProperty.bind(this); 27 | this.setProperty.bind(this); 28 | this.updateProperty.bind(this); 29 | this.onlineDevice.bind(this); 30 | this.reportProperty.bind(this); 31 | } catch (error) { 32 | errorLogger.error('Error to init configuration:',error); 33 | } 34 | } 35 | /** 36 | * @property driverInfo 37 | * @public 38 | * @returns this._driverInfo in JSON String format 39 | */ 40 | get driverInfo() { 41 | return JSON.stringify(this._driverInfo); 42 | } 43 | 44 | /** 45 | * @property meterList 46 | * @public 47 | * @returns this._meterList in JSON String format 48 | */ 49 | get meterList() { 50 | return this._meterList; 51 | } 52 | 53 | 54 | /** 55 | * @property reportInterval 56 | * @public 57 | * @returns this._reportInterval in number format 58 | */ 59 | get reportInterval() { 60 | return this._reportInterval; 61 | } 62 | 63 | /** 64 | * @method _getAllProperties 65 | * @private 66 | * @returns {Array} propertyAlist- Array of String format with all properties 67 | */ 68 | _getAllProperties(){ 69 | try { 70 | let propertyList=[], 71 | properties=this._model.properties, 72 | proLength=properties.length; 73 | 74 | for (let i=0;i { 24 | if (err !== null) { 25 | errorLogger.error('port open error:', err); 26 | process.exit(1); 27 | } 28 | }); 29 | this._concat = new ConcatParser({ 'boundary': [0x16] }); 30 | this._parser = this._port.pipe(this._concat); 31 | this._meterList = configuration.meterList; 32 | this._meterListLength = this._meterList.length; 33 | 34 | //here calls parser to concat data and update relative value in configuration class 35 | this._parser.on('data', (data) => { 36 | let cmd = bufToCmd(data); 37 | 38 | if (cmd) { 39 | this._updateValue(cmd, configuration); 40 | } 41 | 42 | }); 43 | this._yld = this._fetchValue(); 44 | this._fetchInterval = setInterval(() => { 45 | if (this._meterListLength > 0) { 46 | let buf = this._yld.next().value; 47 | 48 | if (buf.length > 1) { 49 | this._port.write(buf, (err) => { 50 | if (err) { 51 | errorLogger.error('fetch meter list error: ', err); 52 | } 53 | }); 54 | } 55 | 56 | } 57 | }, this._updateInterval); 58 | 59 | this._initPort.bind(this); 60 | this._updateValue.bind(this); 61 | this._fetchValue.bind(this); 62 | this.closePort.bind(this); 63 | } 64 | 65 | /** 66 | * @method _initPort 67 | * @private 68 | * @summary update port value of dltdriver from configuration file 69 | */ 70 | _initPort(config) { 71 | try { 72 | 73 | let driverInfo = JSON.parse(config['driverInfo']); 74 | 75 | if (driverInfo) { 76 | let rawPortName = driverInfo['portName']; 77 | 78 | this._portName = rawPortName.replace('tty', '/dev/tty'); 79 | this._updateInterval = driverInfo['updateInterval']; 80 | this._portSet['baudRate'] = driverInfo['baudRate']; 81 | this._portSet['dataBits'] = driverInfo['dataBits']; 82 | this._portSet['parity'] = driverInfo['parity']; 83 | this._portSet['stopBits'] = driverInfo['stopBits']; 84 | this._portSet['autoOpen'] = driverInfo['autoOpen']; 85 | 86 | } 87 | } catch (error) { 88 | errorLogger.error('Error to init port in dltDriver:',error); 89 | process.exit(2); 90 | } 91 | } 92 | /** 93 | * @method _updateValue - to update meterlist value in configuration 94 | * @private 95 | * @param {Object} cmd - a custom formed object,see parser 96 | * @param {Object} configuration -see configuration file 97 | * @summary translate from cmd to configuration format and call configuration to update 98 | */ 99 | _updateValue(cmd, configuration) { 100 | if (cmd !== {} && cmd['sn'] && cmd['data'] && cmd['data']['propertyName'] !== 'errCode') { 101 | 102 | let msg = {}, 103 | property = {}, 104 | data = cmd['data']; 105 | 106 | msg['meterSn'] = cmd['sn']; 107 | msg['properties'] = []; 108 | property['identifier'] = data['propertyName']; 109 | property['value'] = data['value']; 110 | msg['properties'].push(property); 111 | configuration.updateProperty(JSON.stringify(msg)); 112 | } 113 | 114 | } 115 | /** 116 | * @method *_fetchValue - yield function to feed driver 117 | * @private 118 | * @summary yield all properties from meterList and generate command for driver to call port and send 119 | */ 120 | *_fetchValue() { 121 | const cmd = { 122 | 'sn': '0000000000', 123 | 'cmd': '11', 124 | 'data': { 125 | 'status': true, 126 | 'propertyName': 'elecUa', 127 | 'value': 0 128 | } 129 | }, 130 | meterList = []; 131 | 132 | for (let i = 0; i < this._meterListLength; i++) { 133 | meterList.push(JSON.parse(this._meterList[i])); 134 | } 135 | let index = 0; 136 | 137 | while (true) { 138 | let meter = meterList[index], 139 | properties = meter['properties'], 140 | propLength = properties.length; 141 | 142 | cmd['sn'] = meter['meterSn']; 143 | cmd['cmd'] = '11'; 144 | 145 | 146 | for (let i = 0; i < propLength; i++) { 147 | let property = properties[i]; 148 | 149 | cmd['data']['propertyName'] = property['identifier']; 150 | cmd['data']['status'] = true; 151 | cmd['data']['value'] = 0; 152 | yield cmdToBuf(cmd); 153 | } 154 | index++; 155 | if (index >= this._meterList.length) { 156 | index = 0; 157 | } 158 | } 159 | 160 | } 161 | /** 162 | * @method closePort 163 | * @public 164 | * @summary close the port 165 | */ 166 | closePort(){ 167 | this._port.close(); 168 | } 169 | } 170 | 171 | module.exports = DltDriver; 172 | -------------------------------------------------------------------------------- /src/dltparser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const logger=require('./logger'), 4 | infoLogger=logger.getLogger('info'), 5 | // fix buf data format, see dlt 645 for detail 6 | preBuf = Buffer.alloc(4).fill('fe', 'hex'), 7 | divBuf = Buffer.from([0x68]), 8 | endBuf = Buffer.from([0x16]), 9 | 10 | //fix property Code 11 | propertyCode = { 12 | 'elecFh': '02800002', 13 | 'elecFr': '02030000', 14 | 'elecFra': '02030100', 15 | 'elecFrb': '02030200', 16 | 'elecFrc': '02030300', 17 | 'elecPf': '02060000', 18 | 'elecPq': '02040000', 19 | 'elecPqa': '02040100', 20 | 'elecPqb': '02040200', 21 | 'elecPqc': '02040300', 22 | 'elecCa': '02020100', 23 | 'elecCb': '02020200', 24 | 'elecCc': '02020300', 25 | 'elecUa': '02010100', 26 | 'elecUb': '02010200', 27 | 'elecUc': '02010300', 28 | 'elecAe': '00000000', 29 | 'elecAef': '00010000', 30 | 'elecAea': '00150000', 31 | 'elecAeb': '00290000', 32 | 'elecAec': '003d0000', 33 | 'elecFef': '00020000', 34 | 'elecFea': '00160000', 35 | 'elecFeb': '002a0000', 36 | 'elecFec': '003e0000' 37 | }, 38 | 39 | propertyScale = { 40 | 'elecFh': 0.01, 41 | 'elecFr': 0.0001, 42 | 'elecFra':0.0001, 43 | 'elecFrb':0.0001, 44 | 'elecFrc':0.0001, 45 | 'elecPf': 0.001, 46 | 'elecPq': 0.0001, 47 | 'elecPqa':0.0001, 48 | 'elecPqb':0.0001, 49 | 'elecPqc':0.0001, 50 | 'elecCa': 0.001, 51 | 'elecCb': 0.001, 52 | 'elecCc': 0.001, 53 | 'elecUa': 0.1, 54 | 'elecUb': 0.1, 55 | 'elecUc': 0.1, 56 | 'elecAe': 0.01, 57 | 'elecAef':0.01, 58 | 'elecAea':0.01, 59 | 'elecAeb':0.01, 60 | 'elecAec':0.01, 61 | 'elecFef':0.01, 62 | 'elecFea':0.01, 63 | 'elecFeb':0.01, 64 | 'elecFec':0.01 65 | }; 66 | 67 | /** 68 | * @function cmdToBuf - a parser function to transform custom command to Serialport buffer 69 | * @param {Object} - cmd, a custom format of command 70 | * @returns {Buffer} - A serialport command Buffer 71 | *To use the `dltparser` parser, provide a structured cmd or a Buffer of Dlt protocal 72 | *Bellow are dataformat request 73 | const cmd = { 74 | sn: '3411001043', 75 | cmd: '11', 76 | data: { 77 | status:true, 78 | propertyName: 'elecUa', 79 | value:0 80 | } 81 | } 82 | 83 | let encodedData = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 0x34, 84 | 0x00, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x4d, 0x16]); 85 | */ 86 | //First export function to transfer cmd to buf , cmd should be the same as above format 87 | exports.cmdToBuf = (cmd) => { 88 | //meter sn shall not be longer than 12 bytes or will be cutoff 89 | //the buffer will be filled with 0 if meter sn is shorter than 12 90 | let encodeSn = (sn) => { 91 | const snLength = sn.length, 92 | snBuf = Buffer.alloc(6).fill(0, 'hex'); 93 | 94 | for (let i = 0; i < snLength; i++) { 95 | let stopIndex = snLength - 2 * i; 96 | 97 | if (stopIndex <= 0) { 98 | break; 99 | } 100 | snBuf.write(sn.substring(stopIndex - 2, stopIndex), i, 'hex'); 101 | } 102 | return snBuf; 103 | }, 104 | 105 | // Current V1.0 only support encodeData from master to slave 106 | //with fixed data length 107 | encodeData = () => { 108 | const fixBias = Buffer.from([0x33]), 109 | cBuf = Buffer.from(cmd.cmd, 'hex'), 110 | dataBuf = Buffer.from(propertyCode[cmd.data.propertyName], 'hex'), 111 | lBuf = Buffer.from([dataBuf.length], 'hex'); 112 | 113 | for (let i = 0; i < dataBuf.length; i++) { 114 | dataBuf[i] = dataBuf[i] + fixBias[0]; 115 | } 116 | return Buffer.concat([cBuf, lBuf, dataBuf.reverse()]); 117 | }; 118 | 119 | 120 | try { 121 | const cmdBuf = Buffer.concat([divBuf, encodeSn(cmd.sn), 122 | divBuf, encodeData(cmd.sn)]); 123 | let csBuf = Buffer.from([0x00]); 124 | 125 | for (let i = 0; i < cmdBuf.length; i++) { 126 | csBuf[0] = csBuf[0] + cmdBuf[i]; 127 | } 128 | return Buffer.concat([preBuf, cmdBuf, csBuf, endBuf]); 129 | } catch (e) { 130 | infoLogger.info('parse command to buffer error:',e); 131 | return Buffer.alloc(0); 132 | } 133 | 134 | }; 135 | 136 | /** 137 | * A dltparser module which allows to translate between structured cmd and Buffer 138 | * This version is a sync function ,for async function see promise version 139 | * currently only support limited command code like 11,91,b1 and d1, further support is under developing 140 | * TODO support more Dlt code in the future 141 | * @function bufToCmd - a parser function to transform Serialport buffer back into custom Cmd 142 | * @param {Object} - cmd, a custom format of Buffer comply with Dlt645-2007 143 | * @returns {Object} - A custom serialport command B 144 | * To use the `dltparser` parser, provide a structured cmd or a Buffer of Dlt protocal 145 | * dataformat Bellow are dataformat request 146 | const cmd = { 147 | sn: '3411001043', 148 | cmd: '11', 149 | data: { 150 | status:true, 151 | propertyName: 'elecUa', 152 | value:0 153 | } 154 | } 155 | 156 | let encodedData = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 0x34, 157 | 0x00, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x4d, 0x16]); 158 | 159 | */ 160 | exports.bufToCmd = (buf) => { 161 | //meter sn shall not be longer than 12 bytes or will be cutoff 162 | //the buffer will be filled with 0 if meter sn is shorter than 12 163 | let csCheck=(sbuf,cs)=>{ 164 | let ibuf=Buffer.from(sbuf), 165 | csBuf = Buffer.from([0x00]); 166 | 167 | for (let i = 0; i < ibuf.length; i++) { 168 | csBuf[0] = csBuf[0] + ibuf[i]; 169 | } 170 | return csBuf[0]===cs[0]; 171 | }, 172 | 173 | decodeSn=(sbuf,start,div)=>{ 174 | let ibuf=Buffer.from(sbuf), 175 | snBuf=ibuf.slice(start+1,div), 176 | sn=snBuf.reverse().toString('hex'); 177 | 178 | return sn.substr(sn.search(/[^0]/)); 179 | }, 180 | 181 | decodeData=(sbuf,div)=>{ 182 | let ibuf=Buffer.from(sbuf), 183 | cmd={ 184 | 'cmd':'', 185 | 'data':{ 186 | 'status': false, 187 | 'propertyName':'', 188 | 'value': 0 189 | } 190 | }; 191 | 192 | cmd.cmd=ibuf[div+1].toString(16); 193 | 194 | if (cmd.cmd==='91'||cmd.cmd==='b1'){ 195 | let dataLength=ibuf[div+2], 196 | divBias=div+3, 197 | cmdLength=4, 198 | fixBias = Buffer.from([0x33]), 199 | propBuf=ibuf.slice(divBias,divBias+cmdLength), 200 | dataBuf=ibuf.slice(divBias+cmdLength,divBias+dataLength), 201 | dataValue=0, 202 | cmdStr='', 203 | tem=0; 204 | 205 | for (let i=0;i { 30 | client.connect(server['server']); 31 | }, conInterval); 32 | }); 33 | 34 | //Report online and all properties, and reply all websocked request 35 | client.on('connect', function (connection) { 36 | let ml = configuration.meterList.length, 37 | rp = configuration.reportInterval, 38 | reqMsg = '', 39 | rpIntel = ml * rp * 50; 40 | 41 | conInterval = 20000; 42 | 43 | //console.log('WebSocket Client Connected'); 44 | const sendWebSocket = (message) => { 45 | setTimeout(() => { 46 | wsParser(message, configuration, (resMsg) => { 47 | if (resMsg && resMsg !== {}) { 48 | if (resMsg.length > 0) { 49 | let msgLen = resMsg.length; 50 | 51 | for (let i = 0; i < msgLen; i++) { 52 | if (resMsg[i]['code']!==100000){ 53 | let strMsg = JSON.stringify(resMsg[i]); 54 | 55 | setTimeout(() => { 56 | //console.log('send websocket,' + strMsg); 57 | connection.sendUTF(strMsg, (err) => { 58 | errorLogger.error('send websocket arrayerror,' + err); 59 | }); 60 | 61 | }, 2000); 62 | } 63 | 64 | 65 | } 66 | } else if (resMsg['code']!==100000){ 67 | let strMsg = JSON.stringify(resMsg); 68 | 69 | //console.log('send websocket,' + strMsg); 70 | connection.sendUTF(strMsg, (err) => { 71 | errorLogger.error('send websocket error,' + err); 72 | }); 73 | } 74 | } 75 | }); 76 | }, 2000); 77 | }; 78 | 79 | connection.on('error', function (error) { 80 | errorLogger.error('Connection Error: ' + error); 81 | }); 82 | 83 | //automated reconnect 84 | connection.on('close', function () { 85 | errorLogger.error('Remote Connection Closed'); 86 | conInterval += conInterval; 87 | setTimeout(() => { 88 | client.connect(server['server']); 89 | }, conInterval); 90 | }); 91 | connection.on('message', function (message) { 92 | if (message.type === 'utf8') { 93 | let msg = message.utf8Data; 94 | 95 | sendWebSocket(msg); 96 | } 97 | }); 98 | 99 | reqMsg = JSON.stringify({ 100 | 'method': 'onlineDevice' 101 | }); 102 | 103 | sendWebSocket(reqMsg); 104 | reqMsg = JSON.stringify({ 105 | 'method': 'reportProperty' 106 | }); 107 | setInterval(() => { 108 | sendWebSocket(reqMsg); 109 | }, rpIntel); 110 | }); 111 | client.connect(server['server']); 112 | //system exit process 113 | process.on('uncaughtException', (e) => { 114 | errorLogger.error('Unexpected System error:', e); 115 | dltDriver.closePort(); 116 | process.exit(1000); 117 | }); 118 | process.on('exit', () => { 119 | errorLogger.error('system exit, bye bye'); 120 | }); 121 | process.on('SIGINT', () => { 122 | dltDriver.closePort(); 123 | errorLogger.error('system is closing by order, port closed'); 124 | process.exit(1000); 125 | }); 126 | 127 | } catch (error) { 128 | errorLogger.fatal('System Error:', error); 129 | process.exit(1); 130 | } -------------------------------------------------------------------------------- /src/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | *@function getLogger - function to build a log4js module so as to make logging easier 4 | *@param {string} name - string format of log file 5 | *@example 6 | const logger = require("./logger"); 7 | infoLogger = log4js.getLogger("info"); 8 | traceLogger = log4js.getLogger("trace"); 9 | debugLogger = log4js.getLogger("debug"); 10 | 11 | infoLogger.info("现在时间:",new Date().toLocaleString()); 12 | traceLogger.trace("现在时间:",new Date().toLocaleString()); 13 | debugLogger.debug("现在时间:",new Date().toLocaleString()); 14 | *@auther weizy@126.com 15 | *@version 1.0.0 16 | */ 17 | const log4js = require('log4js'); 18 | 19 | log4js.configure({ 20 | 'replaceConsole':false, 21 | 'appenders':{ 22 | 'stdout':{ 23 | 'type':'console' 24 | }, 25 | 'trace':{ 26 | 'type':'dateFile', 27 | 'filename':'log/log/', 28 | 'pattern':'trace-yyyy-MM-dd.log', 29 | 'alwaysIncludePattern':true 30 | }, 31 | 'debug':{ 32 | 'type':'dateFile', 33 | 'filename':'log/log/', 34 | 'pattern':'debug-yyyy-MM-dd.log', 35 | 'alwaysIncludePattern':true 36 | }, 37 | 'info':{ 38 | 'type':'dateFile', 39 | 'filename':'log/log/', 40 | 'pattern':'info-yyyy-MM-dd.log', 41 | 'alwaysIncludePattern':true 42 | }, 43 | 'warn':{ 44 | 'type':'dateFile', 45 | 'filename':'log/log/', 46 | 'pattern':'warn-yyyy-MM-dd.log', 47 | 'alwaysIncludePattern':true 48 | }, 49 | 'error':{ 50 | 'type':'dateFile', 51 | 'filename':'log/log/', 52 | 'pattern':'err-yyyy-MM-dd.log', 53 | 'alwaysIncludePattern':true 54 | }, 55 | 'fatal':{ 56 | 'type':'dateFile', 57 | 'filename':'log/log/', 58 | 'pattern':'fatal-yyyy-MM-dd.log', 59 | 'alwaysIncludePattern':true 60 | } 61 | }, 62 | 'categories':{ 63 | 'default':{'appenders':['stdout','info'],'level':'info'}, 64 | 'trace':{'appenders':['stdout','trace'],'level':'trace'}, 65 | 'debug':{'appenders':['stdout','debug'],'level':'debug'}, 66 | 'warn':{'appenders':['stdout','warn'],'level':'warn'}, 67 | 'error':{'appenders':['stdout','error'],'level':'error'}, 68 | 'fatal':{'appenders':['stdout','fatal'],'level':'fatal'} 69 | } 70 | }); 71 | 72 | exports.getLogger=function(name){ 73 | return log4js.getLogger(name||'info'); 74 | }; 75 | 76 | /** 77 | *@function useLogger - logger for express project 78 | *@param {expressapp} app - Express format app 79 | *@param {log4js} logger - log4js 80 | *@auther weizy@126.com 81 | *@version 1.0.0 82 | */ 83 | exports.useLogger=function(app,logger){ 84 | app.use(log4js.connectLogger(logger||log4js.getLogger('info'),{ 85 | 'format': '[:remote-addr :method :url :status :response-timems][:referrer HTTP/:http-version :user-agent]' 86 | })); 87 | }; -------------------------------------------------------------------------------- /src/model.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties":[ 3 | { 4 | "identifier":"elecPf", 5 | "dataType":{ 6 | "type":"float" 7 | } 8 | }, 9 | { 10 | "identifier":"elecFh", 11 | "dataType":{ 12 | "type":"float" 13 | } 14 | }, 15 | { 16 | "identifier":"elecFr", 17 | "dataType":{ 18 | "type":"float" 19 | } 20 | }, 21 | { 22 | "identifier":"elecFra", 23 | "dataType":{ 24 | "type":"float" 25 | } 26 | }, 27 | { 28 | "identifier":"elecFrb", 29 | "dataType":{ 30 | "type":"float" 31 | } 32 | }, 33 | { 34 | "identifier":"elecAec", 35 | "dataType":{ 36 | "type":"float" 37 | } 38 | }, 39 | { 40 | "identifier":"elecFef", 41 | "dataType":{ 42 | "type":"float" 43 | } 44 | }, 45 | { 46 | "identifier":"elecFea", 47 | "dataType":{ 48 | "type":"float" 49 | } 50 | }, 51 | { 52 | "identifier":"elecFeb", 53 | "dataType":{ 54 | "type":"float" 55 | } 56 | }, 57 | { 58 | "identifier":"elecFec", 59 | "dataType":{ 60 | "type":"float" 61 | } 62 | }, 63 | { 64 | "identifier":"elecFrc", 65 | "dataType":{ 66 | "type":"float" 67 | } 68 | }, 69 | { 70 | "identifier":"elecPq", 71 | "dataType":{ 72 | "type":"float" 73 | } 74 | }, 75 | { 76 | "identifier":"elecPqa", 77 | "dataType":{ 78 | "type":"float" 79 | } 80 | }, 81 | { 82 | "identifier":"elecPqb", 83 | "dataType":{ 84 | "type":"float" 85 | } 86 | }, 87 | { 88 | "identifier":"elecPqc", 89 | "dataType":{ 90 | "type":"float" 91 | } 92 | }, 93 | { 94 | "identifier":"elecCa", 95 | "dataType":{ 96 | "type":"float" 97 | } 98 | }, 99 | { 100 | "identifier":"elecCb", 101 | "dataType":{ 102 | "type":"float" 103 | } 104 | }, 105 | { 106 | "identifier":"elecCc", 107 | "dataType":{ 108 | "type":"float" 109 | } 110 | }, 111 | { 112 | "identifier":"elecUa", 113 | "dataType":{ 114 | "type":"float" 115 | } 116 | }, 117 | { 118 | "identifier":"elecUb", 119 | "dataType":{ 120 | "type":"float" 121 | } 122 | }, 123 | { 124 | "identifier":"elecUc", 125 | "dataType":{ 126 | "type":"float" 127 | } 128 | }, 129 | { 130 | "identifier":"elecAe", 131 | "dataType":{ 132 | "type":"float" 133 | } 134 | }, 135 | { 136 | "identifier":"elecAef", 137 | "dataType":{ 138 | "type":"float" 139 | } 140 | }, 141 | { 142 | "identifier":"elecAea", 143 | "dataType":{ 144 | "type":"float" 145 | } 146 | }, 147 | { 148 | "identifier":"elecAeb", 149 | "dataType":{ 150 | "type":"float" 151 | } 152 | } 153 | ] 154 | } -------------------------------------------------------------------------------- /src/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "server":"ws://127.0.0.1:17682/" 3 | } -------------------------------------------------------------------------------- /src/wsParser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | *@function wsParser - function to parse Ali Iot Linkedge websocket protocal 4 | *@constructor 5 | *@param {string} msgStr - JSON string form webSocket message 6 | *@param {Object} config - configuration class object 7 | *@param {function} callback - callback function to send message back to server 8 | *@auther weizy@126.com 9 | *@version 1.0.0 10 | */ 11 | 12 | module.exports = function (msgStr, config, cb) { 13 | 14 | let msg = JSON.parse(msgStr), 15 | version = msg.version ? msg.version : '1.0', 16 | message = msg.message ? msg.message : '', 17 | payload = msg.payload ? msg.payload : {}, 18 | code = msg.code ? msg.code : 0, 19 | method = msg.method ? msg.method : ''; 20 | 21 | const callRegDevice = function (regMethod) { 22 | let devices = config.onlineDevice(), 23 | devLength = devices.length, 24 | resList = [], 25 | resMsg = { 26 | 'version': '1.0', 27 | 'method': regMethod, 28 | 'messageId': regMethod === 'onlineDevice' ? 1 : 2, 29 | 'payload': {} 30 | }; 31 | 32 | for (let i = 0; i < devLength; i++) { 33 | Object.assign(resMsg['payload'], devices[i]); 34 | resList.push(JSON.parse(JSON.stringify(resMsg))); 35 | } 36 | return resList; 37 | }, 38 | callReportProperty = function () { 39 | let devices = config.reportProperty(), 40 | devLength = devices.length, 41 | resList = [], 42 | resMsg = { 43 | 'version': '1.0', 44 | 'method': 'reportProperty', 45 | 'messageId': 0, 46 | 'payload': {} 47 | }; 48 | 49 | for (let i = 0; i < devLength; i++) { 50 | //Object.assign(resMsg['payload'], devices[i]); 51 | resMsg['payload'] = devices[i]; 52 | resList.push(JSON.parse(JSON.stringify(resMsg))); 53 | } 54 | return resList; 55 | }, 56 | callGetProperty = function () { 57 | let reqMsg = {}, 58 | resProperties = [], 59 | resMsg = {}; 60 | 61 | reqMsg['productKey'] = payload['productKey']; 62 | reqMsg['deviceName'] = payload['deviceName']; 63 | reqMsg['properties'] = payload['properties']; 64 | resProperties = config.getProperty(JSON.stringify(reqMsg)); 65 | resMsg = { 'code': 0, 'messageId': 3, 'payload': { 'properties': resProperties } }; 66 | 67 | return resMsg; 68 | }, 69 | 70 | 71 | dispatch = function () { 72 | if (code === 0 && message === 'Success') { 73 | let rest = { 74 | 'code': 100000, 75 | 'message': 'Unrecognized method.' 76 | }; 77 | 78 | return cb(rest); 79 | } 80 | 81 | if (version === '1.0') { 82 | if (method) { 83 | if (method === 'onlineDevice') { 84 | let rest = callRegDevice('onlineDevice'); 85 | 86 | return cb(rest); 87 | 88 | } else if (method === 'offlineDevice') { 89 | let rest = callRegDevice('offlineDevice'); 90 | 91 | return cb(rest); 92 | 93 | } else if (method === 'reportProperty') { 94 | let rest = callReportProperty(); 95 | 96 | return cb(rest); 97 | 98 | } else if (method === 'getProperty') { 99 | let rest = callGetProperty(); 100 | 101 | return cb(rest); 102 | } else if (method === 'setProperty') { 103 | let rest = { 104 | 'code': 109003, 105 | 'message': 'failure, property readonly.' 106 | }; 107 | 108 | return cb(rest); 109 | 110 | } else if (method === 'callService') { 111 | let rest = { 112 | 'code': 109005, 113 | 'message': 'The requested service does not exist.' 114 | }; 115 | 116 | return cb(rest); 117 | } 118 | let rest = { 119 | 'code': 100000, 120 | 'message': 'Unrecognized method.' 121 | }; 122 | 123 | return cb(rest); 124 | } 125 | let rest = { 126 | 'code': 100000, 127 | 'message': 'Unrecognized method.' 128 | }; 129 | 130 | return cb(rest); 131 | 132 | } 133 | let rest = { 134 | 'code': 100000, 135 | 'message': 'Unrecognized method.' 136 | }; 137 | 138 | return cb(rest); 139 | 140 | }; 141 | 142 | return dispatch(); 143 | }; -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | nohup node /home/wdsolar/Projects/dlt645Driver/src/index.js & 3 | #use following script to show the pid and kill it if necessary 4 | #ps -e|grep node 5 | 6 | #to start the script when start up, local the path of this script 7 | #crontab -e 8 | #add following lines 9 | #@reboot yourpath/start.sh 10 | -------------------------------------------------------------------------------- /test/concatparser_test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-new */ 2 | 3 | const sinon = require('sinon'); 4 | const ConcatParser = require('../src/concatparser'); 5 | 6 | //const sinonChai = require('sinon-chai'); 7 | const chai = require('chai'); 8 | const expect = chai.expect; 9 | //chai.use(sinonChai); 10 | 11 | 12 | describe('ConcatParser', () => { 13 | 14 | it('should transforms data to strings concated and split with a boundary', () => { 15 | const parser = new ConcatParser({ 16 | boundary: [0x16], 17 | }); 18 | const spy = sinon.spy(); 19 | parser.on('data', spy); 20 | parser.write(Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10])); 21 | //parser.write(Buffer.from([0x00, 0x11, 0x34, 0x00, 0x68, 0x91, 0x06])); 22 | parser.write(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 23 | parser.write(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 24 | expect(spy.getCall(0).args[0]).to.be.eql(Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 25 | 0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 26 | expect(spy.getCall(1).args[0]).to.be.eql(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 27 | expect(spy.calledTwice).to.be.true; 28 | // assert.deepEqual(spy.getCall(1).args[0], Buffer.from('Each and Every One')); 29 | // assert(spy.calledTwice); 30 | }); 31 | 32 | 33 | it('should transforms maxBufferSize data if too much data is written', () => { 34 | const parser = new ConcatParser({ 35 | boundary: [0x16], 36 | maxBufferSize: 15, 37 | }); 38 | const spy = sinon.spy(); 39 | parser.on('data', spy); 40 | parser.write(Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10])); 41 | parser.write(Buffer.from([0x00, 0x11, 0x34, 0x00, 0x68, 0x91, 0x06])); 42 | parser.write(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 43 | parser.write(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 44 | expect(spy.getCall(0).args[0]).to.be.eql(Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 45 | 0x00, 0x11, 0x34, 0x00, 0x68, 0x91, 0x06, 0x34])); 46 | expect(spy.getCall(1).args[0]).to.be.eql(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 47 | expect(spy.calledTwice).to.be.true; 48 | }) 49 | 50 | it('should transforms current data if it takes too much time', () => { 51 | const parser = new ConcatParser({ 52 | boundary: [0x16], 53 | interval: 100, 54 | }); 55 | const spy = sinon.spy(); 56 | parser.on('data', spy); 57 | parser.write(Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10])); 58 | parser.write(Buffer.from([0x00, 0x11, 0x34, 0x00, 0x68, 0x91, 0x06])); 59 | setTimeout(() => { 60 | parser.write(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 61 | expect(spy.getCall(0).args[0]).to.be.eql(Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 62 | 0x00, 0x11, 0x34, 0x00, 0x68, 0x91, 0x06])); 63 | expect(spy.getCall(1).args[0]).to.be.eql(Buffer.from([0x34, 0x34, 0x35, 0x49, 0x56, 0x6e, 0x16])); 64 | }, 200); 65 | }) 66 | 67 | }) -------------------------------------------------------------------------------- /test/configuration_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const path = require('path'); 5 | const Configuration = require('../src/configuration'); 6 | 7 | describe('configuration tests', () => { 8 | const configFile = path.join(__dirname, 'testconfig.json'); 9 | const modelFile = path.join(__dirname, 'testmodel.json'); 10 | const configuration = new Configuration(configFile, modelFile); 11 | const driveInfo = JSON.parse(JSON.stringify({ 12 | "portName": "ttyS4", 13 | "baudRate": 1200, 14 | "dataBits": 8, 15 | "parity": "even", 16 | "stopBits": 1, 17 | "autoOpen": true, 18 | "updateInterval": 3000 19 | })); 20 | 21 | 22 | 23 | it('should pass this canary test', () => { 24 | expect(true).to.be.true; 25 | }); 26 | 27 | it('configuration should create initial configuration correctly', () => { 28 | 29 | expect(configuration.reportInterval).to.be.eql(10000); 30 | expect(JSON.parse(configuration.driverInfo)).to.be.eql(driveInfo); 31 | expect(configuration.meterList.length).to.be.eql(2); 32 | }); 33 | 34 | it('configuration should build correct device model with meters', () => { 35 | 36 | let meterList = configuration.meterList; 37 | expect(JSON.parse(meterList[0]).productKey).to.be.eql("a1CLH9cvgoK"); 38 | expect(JSON.parse(meterList[0]).deviceName).to.be.eql("tstMeter"); 39 | expect(JSON.parse(meterList[0]).meterSn).to.be.eql("3411001043"); 40 | expect(JSON.parse(meterList[0]).properties[0].identifier).to.be.eql("elecPf"); 41 | expect(JSON.parse(meterList[0]).properties[0].type).to.be.eql("int"); 42 | expect(JSON.parse(meterList[0]).properties[0].value).to.be.eql(0); 43 | expect(JSON.parse(meterList[1]).productKey).to.be.eql("a1CLH9cvgoK"); 44 | expect(JSON.parse(meterList[1]).deviceName).to.be.eql("virtualMeter"); 45 | expect(JSON.parse(meterList[1]).meterSn).to.be.eql("0000000000"); 46 | expect(JSON.parse(meterList[0]).properties[1].identifier).to.be.eql("elecFh"); 47 | expect(JSON.parse(meterList[0]).properties[1].type).to.be.eql("int"); 48 | expect(JSON.parse(meterList[0]).properties[1].value).to.be.eql(0); 49 | }); 50 | 51 | it('private method getAllProperties should return all property list', () => { 52 | 53 | let propertis = configuration._getAllProperties(); 54 | let resList = ['elecPf', 'elecFh', 'elecFr', 'elecFra', 'elecFrb','elecFrc','elecPq','elecPqa', 55 | 'elecPqb','elecPqc','elecCa','elecCb','elecCc','elecUa','elecUb','elecUc','elecAe','elecAef', 56 | 'elecAea','elecAeb','elecAec','elecFef','elecFea','elecFeb','elecFec']; 57 | expect(propertis).to.be.eql(resList); 58 | 59 | }); 60 | 61 | it('getProperty should return correct value string and ignore invalid property', () => { 62 | 63 | let msg = JSON.stringify({ 64 | "productKey": "a1CLH9cvgoK", 65 | "deviceName": "tstMeter", 66 | "properties": ["elecPf", "elecFh", "elecUa"] 67 | }); 68 | let msg2 = JSON.stringify({ 69 | "productKey": "a1CLH9cvgoK", 70 | "deviceName": "virtualMeter", 71 | "properties": ["elecPf", "elecFh", "elecUa", "invalidProperty"] 72 | }); 73 | 74 | let resmsg = [{ "identifier": "elecPf", "type": "int", "value": 0 }, 75 | { "identifier": "elecFh", "type": "int", "value": 0 }, 76 | { "identifier": "elecUa", "type": "int", "value": 0 }]; 77 | 78 | let response = configuration.getProperty(msg); 79 | let response2 = configuration.getProperty(msg2); 80 | expect(response).to.be.eql(resmsg); 81 | expect(response2).to.be.eql(resmsg); 82 | 83 | }); 84 | 85 | it('setProperty should set correct value string and ignore invalid property', () => { 86 | 87 | let msg = JSON.stringify({ 88 | "productKey": "a1CLH9cvgoK", 89 | "deviceName": "tstMeter", 90 | "properties": [{ "identifier": "elecPf", "type": "int", "value": 10 }, 91 | { "identifier": "elecFh", "type": "int", "value": 100 }, 92 | { "identifier": "elecUa", "type": "int", "value": 1000 }, 93 | { "identifier": "invalidProperty", "type": "int", "value": 10000 }] 94 | }); 95 | let msg2 = JSON.stringify({ 96 | "productKey": "a1CLH9cvgoK", 97 | "deviceName": "tstMeter", 98 | "properties": ["elecPf", "elecFh", "elecUa", "invalidProperty"] 99 | }); 100 | 101 | let resmsg = [{ "identifier": "elecPf", "type": "int", "value": 10 }, 102 | { "identifier": "elecFh", "type": "int", "value": 100 }, 103 | { "identifier": "elecUa", "type": "int", "value": 1000 }]; 104 | 105 | configuration.setProperty(msg); 106 | let response = configuration.getProperty(msg2); 107 | expect(response).to.be.eql(resmsg); 108 | 109 | }); 110 | 111 | it('updateProperty should set correct value with meterSn property', () => { 112 | 113 | let msg = JSON.stringify({ 114 | "meterSn": "3411001043", 115 | "properties": [{ "identifier": "elecPf", "value": 20 }, 116 | { "identifier": "elecFh","value": 200 }, 117 | { "identifier": "elecUa","value": 2000 }, 118 | { "identifier": "invalidProperty" ,"value": 20000 }] 119 | }); 120 | let msg2 = JSON.stringify({ 121 | "productKey": "a1CLH9cvgoK", 122 | "deviceName": "tstMeter", 123 | "properties": ["elecPf", "elecFh", "elecUa", "invalidProperty"] 124 | }); 125 | 126 | let resmsg = [{ "identifier": "elecPf", "type": "int", "value": 20 }, 127 | { "identifier": "elecFh", "type": "int", "value": 200 }, 128 | { "identifier": "elecUa", "type": "int", "value": 2000 }]; 129 | 130 | configuration.updateProperty(msg); 131 | let response = configuration.getProperty(msg2); 132 | expect(response).to.be.eql(resmsg); 133 | 134 | }); 135 | 136 | it('onlineDevice should return all device Key and Name', () => { 137 | 138 | let resmsg = [{ "deviceName": "tstMeter", "productKey": "a1CLH9cvgoK" }, 139 | { "deviceName": "virtualMeter", "productKey": "a1CLH9cvgoK" }]; 140 | let response = configuration.onlineDevice(); 141 | expect(response).to.be.eql(resmsg); 142 | }); 143 | 144 | 145 | }); -------------------------------------------------------------------------------- /test/dltdriver_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const expect = require('chai').expect; 5 | //const SerialPort = require('serialport'); 6 | const sinon = require('sinon'); 7 | const DltDriver = require('../src/dltdriver'); 8 | const Configuration = require('../src/configuration'); 9 | 10 | describe('dltdriver tests', () => { 11 | 12 | let sandbox; 13 | const configFile = path.join(__dirname, 'testconfig.json'); 14 | const modelFile = path.join(__dirname, 'testmodel.json'); 15 | const configuration = new Configuration(configFile, modelFile); 16 | const dltDriver = new DltDriver(configuration); 17 | // stubConfig.onlineDevice = sandbox.stub().returns(stubMsg); 18 | beforeEach(() => { 19 | sandbox = sinon.createSandbox(); 20 | 21 | 22 | }) 23 | afterEach(() => { 24 | sandbox.restore(); 25 | }) 26 | 27 | it('dltdriver should create dltdriver object successful', () => { 28 | const portSet = { 29 | baudRate: 1200, 30 | dataBits: 8, 31 | parity: "even", 32 | stopBits: 1, 33 | autoOpen: true 34 | }; 35 | expect(dltDriver._portName).to.be.eql('/dev/ttyS4'); 36 | expect(dltDriver._portSet).to.be.eql(portSet); 37 | expect(dltDriver._updateInterval).to.be.eql(3000); 38 | }); 39 | 40 | it('dltdriver _updateValue should update configuration value successful', () => { 41 | const cmd = { 42 | sn: '3411001043', 43 | cmd: '11', 44 | data: { 45 | status: true, 46 | propertyName: 'elecUa', 47 | value: 125 48 | } 49 | }; 50 | let reqMsg = JSON.stringify({ 51 | "productKey": "a1CLH9cvgoK", 52 | "deviceName": "tstMeter", 53 | "properties": ["elecUa"] 54 | }); 55 | let resmsg = [{ "identifier": "elecUa", "type": "int", "value": 125 }]; 56 | dltDriver._updateValue(cmd, configuration); 57 | let msg = configuration.getProperty(reqMsg); 58 | expect(msg).to.be.eql(resmsg); 59 | 60 | }); 61 | 62 | it('dltdriver *_fetchValue should yield value successful', () => { 63 | let reqMsg = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 64 | 0x34, 0x00, 0x68, 0x11, 0x04, 0x33, 0x33, 0x39, 0x35, 0x51, 0x16]); 65 | let reqMsg2 = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 66 | 0x34, 0x00, 0x68, 0x11, 0x04, 0x35, 0x33, 0xb3, 0x35, 0xcd, 0x16]); 67 | let yld = dltDriver._fetchValue(); 68 | expect(yld.next().value).to.be.eql(reqMsg); 69 | expect(yld.next().value).to.be.eql(reqMsg2); 70 | 71 | }); 72 | 73 | }); 74 | -------------------------------------------------------------------------------- /test/dltparser_test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const dltParser = require('../src/dltparser'); 3 | 4 | describe('Sync Encoder test', () => { 5 | it('should encode correct from cmd to buf', () => { 6 | const cmd = { 7 | sn: '3411001043', 8 | cmd: '11', 9 | data: { 10 | status:true, 11 | propertyName: 'elecUa', 12 | value:0 13 | } 14 | } 15 | let encodedData = Buffer.from([0xfe, 0xfe, 0xfe, 0xfe, 0x68, 0x43, 0x10, 0x00, 0x11, 0x34, 16 | 0x00, 0x68, 0x11, 0x04, 0x33, 0x34, 0x34, 0x35, 0x4d, 0x16]); 17 | expect(dltParser.cmdToBuf(cmd)).to.eql(encodedData); 18 | }); 19 | 20 | it('should return empty Buffer if cmd of wrong format', () => { 21 | const cmd ={}; 22 | expect(dltParser.cmdToBuf(cmd)).to.eql(Buffer.alloc(0)); 23 | }); 24 | }); 25 | 26 | describe('Sync Decoder test', () => { 27 | it('should decode correct from buf to cmd', () => { 28 | let cmd = { 29 | sn: '3411001043', 30 | cmd: '91', 31 | data: { 32 | status:true, 33 | propertyName: 'elecUa', 34 | value:2297 35 | } 36 | } 37 | let buf= Buffer.from([0xfe ,0xfe ,0xfe ,0xfe ,0x68 ,0x43 ,0x10 ,0x00 ,0x11 ,0x34 ,0x00 , 38 | 0x68 ,0x91 ,0x06 ,0x33 ,0x34 ,0x34 ,0x35 ,0xca ,0x55 ,0xee ,0x16]); 39 | expect(dltParser.bufToCmd(buf)).to.eql(cmd); 40 | }); 41 | 42 | it('should return error message if buf of wrong format', () => { 43 | const buf =undefined; 44 | let cmd={ 45 | cmd: '', 46 | data: { 47 | status: false, 48 | propertyName:'errCode', 49 | value: 'TypeError: Cannot read property \'indexOf\' of undefined' 50 | } 51 | } 52 | 53 | expect(dltParser.bufToCmd(buf)).to.eql(cmd); 54 | }); 55 | 56 | it('should return csCheck error if cs is incorectt', () => { 57 | const buf = Buffer.from([0xfe ,0xfe ,0xfe ,0xfe ,0x68 ,0x43 ,0x10 ,0x00 ,0x11 ,0x34 ,0x00 , 58 | 0x68 ,0xd1 ,0x06 ,0x33 ,0x34 ,0x34 ,0x35 ,0xca ,0x55 ,0xee ,0x16]); 59 | let cmd={ 60 | cmd: '', 61 | data: { 62 | status: false, 63 | propertyName:'errCode', 64 | value: 'csCheck Error' 65 | } 66 | } 67 | 68 | expect(dltParser.bufToCmd(buf)).to.eql(cmd); 69 | }); 70 | 71 | it('should return d1 error code', () => { 72 | const buf = Buffer.from([0xfe ,0xfe ,0xfe ,0xfe ,0x68 ,0x43 ,0x10 ,0x00 ,0x11 ,0x34 ,0x00 , 73 | 0x68 ,0xd1 ,0x01 ,0x31,0x6b ,0x16]); 74 | let cmd={ 75 | cmd: 'd1', 76 | sn: '3411001043', 77 | data: { 78 | status: false, 79 | propertyName:'errCode', 80 | value: '31' 81 | } 82 | } 83 | 84 | expect(dltParser.bufToCmd(buf)).to.eql(cmd); 85 | }); 86 | }); -------------------------------------------------------------------------------- /test/testconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "driverInfo": { 3 | "portName":"ttyS4", 4 | "baudRate": 1200, 5 | "dataBits": 8, 6 | "parity": "even", 7 | "stopBits": 1, 8 | "autoOpen":true, 9 | "updateInterval":3000 10 | }, 11 | "meterList": [ 12 | { 13 | "productKey":"a1CLH9cvgoK", 14 | "deviceName":"tstMeter", 15 | "meterSn":"3411001043" 16 | }, 17 | { 18 | "productKey":"a1CLH9cvgoK", 19 | "deviceName":"virtualMeter", 20 | "meterSn":"0000000000" 21 | } 22 | ], 23 | "reportInterval":10000 24 | } -------------------------------------------------------------------------------- /test/testmodel.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties":[ 3 | { 4 | "identifier":"elecPf", 5 | "dataType":{ 6 | "type":"int" 7 | } 8 | }, 9 | { 10 | "identifier":"elecFh", 11 | "dataType":{ 12 | "type":"int" 13 | } 14 | }, 15 | { 16 | "identifier":"elecFr", 17 | "dataType":{ 18 | "type":"int" 19 | } 20 | }, 21 | { 22 | "identifier":"elecFra", 23 | "dataType":{ 24 | "type":"int" 25 | } 26 | }, 27 | { 28 | "identifier":"elecFrb", 29 | "dataType":{ 30 | "type":"int" 31 | } 32 | }, 33 | { 34 | "identifier":"elecFrc", 35 | "dataType":{ 36 | "type":"int" 37 | } 38 | }, 39 | { 40 | "identifier":"elecPq", 41 | "dataType":{ 42 | "type":"int" 43 | } 44 | }, 45 | { 46 | "identifier":"elecPqa", 47 | "dataType":{ 48 | "type":"int" 49 | } 50 | }, 51 | { 52 | "identifier":"elecPqb", 53 | "dataType":{ 54 | "type":"int" 55 | } 56 | }, 57 | { 58 | "identifier":"elecPqc", 59 | "dataType":{ 60 | "type":"int" 61 | } 62 | }, 63 | { 64 | "identifier":"elecCa", 65 | "dataType":{ 66 | "type":"int" 67 | } 68 | }, 69 | { 70 | "identifier":"elecCb", 71 | "dataType":{ 72 | "type":"int" 73 | } 74 | }, 75 | { 76 | "identifier":"elecCc", 77 | "dataType":{ 78 | "type":"int" 79 | } 80 | }, 81 | { 82 | "identifier":"elecUa", 83 | "dataType":{ 84 | "type":"int" 85 | } 86 | }, 87 | { 88 | "identifier":"elecUb", 89 | "dataType":{ 90 | "type":"int" 91 | } 92 | }, 93 | { 94 | "identifier":"elecUc", 95 | "dataType":{ 96 | "type":"int" 97 | } 98 | }, 99 | { 100 | "identifier":"elecAe", 101 | "dataType":{ 102 | "type":"int" 103 | } 104 | }, 105 | { 106 | "identifier":"elecAef", 107 | "dataType":{ 108 | "type":"int" 109 | } 110 | }, 111 | { 112 | "identifier":"elecAea", 113 | "dataType":{ 114 | "type":"int" 115 | } 116 | }, 117 | { 118 | "identifier":"elecAeb", 119 | "dataType":{ 120 | "type":"int" 121 | } 122 | }, 123 | { 124 | "identifier":"elecAec", 125 | "dataType":{ 126 | "type":"int" 127 | } 128 | }, 129 | { 130 | "identifier":"elecFef", 131 | "dataType":{ 132 | "type":"int" 133 | } 134 | }, 135 | { 136 | "identifier":"elecFea", 137 | "dataType":{ 138 | "type":"int" 139 | } 140 | }, 141 | { 142 | "identifier":"elecFeb", 143 | "dataType":{ 144 | "type":"int" 145 | } 146 | }, 147 | { 148 | "identifier":"elecFec", 149 | "dataType":{ 150 | "type":"int" 151 | } 152 | } 153 | ] 154 | } -------------------------------------------------------------------------------- /test/wsparser_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const sinon = require('sinon'); 5 | const wsParser = require('../src/wsParser'); 6 | const Configuration = require('../src/configuration'); 7 | 8 | describe('wsParser tests', () => { 9 | 10 | let sandbox; 11 | beforeEach(() => { 12 | sandbox = sinon.createSandbox(); 13 | 14 | }) 15 | afterEach(() => { 16 | sandbox.restore(); 17 | }) 18 | 19 | it('should register device online', (done) => { 20 | let messageStr = JSON.stringify({ 21 | "method": "onlineDevice" 22 | }); 23 | let stubMsg = [{ "deviceName": "tstMeter", "productKey": "a1CLH9cvgoK" }, 24 | { "deviceName": "virtualMeter", "productKey": "a1CLH9cvgoK" }] 25 | let resmsg = [{ 26 | version: '1.0', 27 | method: 'onlineDevice', 28 | messageId: 1, 29 | payload: { deviceName: 'tstMeter', productKey: 'a1CLH9cvgoK' } 30 | }, 31 | { 32 | version: '1.0', 33 | method: 'onlineDevice', 34 | messageId: 1, 35 | payload: { productKey: 'a1CLH9cvgoK', deviceName: 'virtualMeter' } 36 | }]; 37 | // 38 | // const configFile = path.join(__dirname, 'testconfig.json'); 39 | // const modelFile = path.join(__dirname, 'testmodel.json'); 40 | // const configuration = new Configuration(configFile, modelFile); 41 | const stubConfig = sandbox.createStubInstance(Configuration); 42 | stubConfig.onlineDevice = sandbox.stub().returns(stubMsg); 43 | let cb = (msg) => { 44 | expect(msg).to.be.eql(resmsg); 45 | done(); 46 | } 47 | wsParser(messageStr, stubConfig, cb); 48 | }); 49 | it('should register device offline', (done) => { 50 | let messageStr = JSON.stringify({ 51 | "method": "offlineDevice" 52 | }); 53 | let stubMsg = [{ "deviceName": "tstMeter", "productKey": "a1CLH9cvgoK" }, 54 | { "deviceName": "virtualMeter", "productKey": "a1CLH9cvgoK" }] 55 | let resmsg = [{ 56 | version: '1.0', 57 | method: 'offlineDevice', 58 | messageId: 2, 59 | payload: { deviceName: 'tstMeter', productKey: 'a1CLH9cvgoK' } 60 | }, 61 | { 62 | version: '1.0', 63 | method: 'offlineDevice', 64 | messageId: 2, 65 | payload: { productKey: 'a1CLH9cvgoK', deviceName: 'virtualMeter' } 66 | }]; 67 | // const path=require('path'); 68 | // const configFile = path.join(__dirname, 'testconfig.json'); 69 | // const modelFile = path.join(__dirname, 'testmodel.json'); 70 | // const configuration = new Configuration(configFile, modelFile); 71 | const stubConfig = sandbox.createStubInstance(Configuration); 72 | stubConfig.onlineDevice = sandbox.stub().returns(stubMsg); 73 | let cb = (msg) => { 74 | expect(msg).to.be.eql(resmsg); 75 | done(); 76 | } 77 | wsParser(messageStr, stubConfig, cb); 78 | }); 79 | it('should report all devices with all properties', (done) => { 80 | let messageStr = JSON.stringify({ 81 | "method": "reportProperty" 82 | }); 83 | let stubMsg = [{ 84 | "deviceName": "tstMeter", 85 | "productKey": "a1CLH9cvgoK", 86 | "properties": [{ "identifier": "elecPf", "type": "int", "value": 10 }, 87 | { "identifier": "elecFh", "type": "int", "value": 100 }, 88 | { "identifier": "elecFr", "type": "int", "value": 1000 }, 89 | { "identifier": "elecFra", "type": "int", "value": 10000 }] 90 | }, 91 | { 92 | "deviceName": "virtualMeter", 93 | "productKey": "a1CLH9cvgoK", 94 | "properties": [{ "identifier": "elecPf", "type": "int", "value": 20 }, 95 | { "identifier": "elecFh", "type": "int", "value": 200 }, 96 | { "identifier": "elecFr", "type": "int", "value": 2000 }, 97 | { "identifier": "elecFra", "type": "int", "value": 20000 }] 98 | }]; 99 | 100 | let resmsg = [{ 101 | version: '1.0', 102 | method: 'reportProperty', 103 | messageId: 0, 104 | payload: stubMsg[0] 105 | }, 106 | { 107 | version: '1.0', 108 | method: 'reportProperty', 109 | messageId: 0, 110 | payload: stubMsg[1] 111 | }]; 112 | // const path = require('path'); 113 | // const configFile = path.join(__dirname, 'testconfig.json'); 114 | // const modelFile = path.join(__dirname, 'testmodel.json'); 115 | // const configuration = new Configuration(configFile, modelFile); 116 | const stubConfig = sandbox.createStubInstance(Configuration); 117 | stubConfig.reportProperty = sandbox.stub().returns(stubMsg); 118 | let cb = (msg) => { 119 | expect(msg).to.be.eql(resmsg); 120 | done(); 121 | } 122 | // wsParser(messageStr, configuration, cb); 123 | wsParser(messageStr, stubConfig, cb); 124 | }); 125 | it('should report specific properties when receive getProperty command', (done) => { 126 | let messageStr = JSON.stringify({ 127 | "version": "1.0", 128 | "method": "getProperty", 129 | "messageId": 3, 130 | "payload": { 131 | "productKey": "a1CLH9cvgoK", 132 | "deviceName": "tstMeter", 133 | "properties": ["elecPf", "elecFh","elecFr","elecFra"] 134 | } 135 | }); 136 | let stubMsg = [{"identifier":"elecPf","type":"int","value":0}, 137 | {"identifier":"elecFh","type":"int","value":0}, 138 | {"identifier":"elecFr","type":"int","value":0}, 139 | {"identifier":"elecFra","type":"int","value":0}]; 140 | 141 | let resmsg = { 142 | "code":0, 143 | "messageId":3, 144 | "payload":{ 145 | "properties":[{"identifier":"elecPf","type":"int","value":0}, 146 | {"identifier":"elecFh","type":"int","value":0}, 147 | {"identifier":"elecFr","type":"int","value":0}, 148 | {"identifier":"elecFra","type":"int","value":0}] 149 | } 150 | }; 151 | // const path = require('path'); 152 | // const configFile = path.join(__dirname, 'testconfig.json'); 153 | // const modelFile = path.join(__dirname, 'testmodel.json'); 154 | // const configuration = new Configuration(configFile, modelFile); 155 | const stubConfig = sandbox.createStubInstance(Configuration); 156 | stubConfig.getProperty = sandbox.stub().returns(stubMsg); 157 | let cb = (msg) => { 158 | expect(msg).to.be.eql(resmsg); 159 | done(); 160 | } 161 | // wsParser(messageStr, configuration, cb); 162 | wsParser(messageStr, stubConfig, cb); 163 | }); 164 | it('should return specific object if it is just Success response', (done) => { 165 | let messageStr = JSON.stringify({ 166 | "code": 0, 167 | "messageId": 1, 168 | "message": "Success", 169 | "payload": { } 170 | }); 171 | 172 | let resmsg = { 173 | "code": 100000, 174 | "message": "Unrecognized method.", 175 | }; 176 | // const path = require('path'); 177 | // const configFile = path.join(__dirname, 'testconfig.json'); 178 | // const modelFile = path.join(__dirname, 'testmodel.json'); 179 | // const configuration = new Configuration(configFile, modelFile); 180 | const stubConfig = sandbox.createStubInstance(Configuration); 181 | let cb = (msg) => { 182 | expect(msg).to.be.eql(resmsg); 183 | done(); 184 | } 185 | // wsParser(messageStr, configuration, cb); 186 | wsParser(messageStr, stubConfig, cb); 187 | }); 188 | it('should return error message if wrong message is received', (done) => { 189 | let messageStr = JSON.stringify({ 190 | "code": 0, 191 | "messageId": 2, 192 | "method": "reportEvent", 193 | "payload": {} 194 | }); 195 | 196 | let resmsg = { 197 | "code":100000, 198 | "message":"Unrecognized method." 199 | }; 200 | // const path = require('path'); 201 | // const configFile = path.join(__dirname, 'testconfig.json'); 202 | // const modelFile = path.join(__dirname, 'testmodel.json'); 203 | // const configuration = new Configuration(configFile, modelFile); 204 | const stubConfig = sandbox.createStubInstance(Configuration); 205 | let cb = (msg) => { 206 | expect(msg).to.be.eql(resmsg); 207 | done(); 208 | } 209 | // wsParser(messageStr, configuration, cb); 210 | wsParser(messageStr, stubConfig, cb); 211 | }); 212 | it('should return readonly message if setproperty is received', (done) => { 213 | let messageStr = JSON.stringify({ 214 | "version": '1.0', 215 | "messageId": 2, 216 | "method": "setProperty", 217 | "payload": {} 218 | }); 219 | 220 | let resmsg = { 221 | "code":109003, 222 | "message":"failure, property readonly." 223 | }; 224 | // const path = require('path'); 225 | // const configFile = path.join(__dirname, 'testconfig.json'); 226 | // const modelFile = path.join(__dirname, 'testmodel.json'); 227 | // const configuration = new Configuration(configFile, modelFile); 228 | const stubConfig = sandbox.createStubInstance(Configuration); 229 | let cb = (msg) => { 230 | expect(msg).to.be.eql(resmsg); 231 | done(); 232 | } 233 | // wsParser(messageStr, configuration, cb); 234 | wsParser(messageStr, stubConfig, cb); 235 | }); 236 | }); --------------------------------------------------------------------------------