浏览代码

Merge pull request #485 from EMQmyd/func

feat():functions metadata and installed
EMQmyd 4 年之前
父节点
当前提交
7db235f50c

+ 57 - 12
docs/zh_CN/plugins/overview.md

@@ -281,11 +281,38 @@ Kuiper 具有许多内置函数,可以对数据执行计算。(具体文档参
 
 元数据文件格式为 JSON,主要分成以下部分:
 
+#### about
+
+* trial:表示插件是否可用
+
+* author
+
+  这部分包含了插件的作者信息,插件开发者可以视情况提供这些信息,这部分信息会被展示在管理控制台的插件信息列表上。
+
+     * name:名字
+     * email:电子邮件地址
+     * company:公司名称
+     * website:公司网站地址
+
+* helpUrl
+
+  该插件的帮助文件地址,控制台会根据语言的支持情况,链接到对应的帮助文档中。
+
+     * en_US:英文文档帮助地址
+     * zh_CN:中文文档帮助地址
+
+* description
+
+  该插件的简单描述,控制台支持多种语言。
+
+  * en_US:英文描述
+  * zh_CN:中文描述
+
+#### functions
+
 - name:属性名称;**该字段必须提供;**
-- control:控件类型,控制在界面中显示的控件类型;**该字段必须提供;**
-  - text:文本输入框
 - example:样例
-- hint:控件的提示信息;该字段可选;
+- hint:函数的提示信息;该字段可选;
   - en_US
   - zh_CN
 
@@ -294,15 +321,33 @@ Kuiper 具有许多内置函数,可以对数据执行计算。(具体文档参
 以下为样例元数据文件。
 
 ```json
-[{
-	"name": "avg",
-	"control": "text",
-	"example": "avg(col1)",
-	"hint": {
-		"en_US": "The file path for saving the result",
-		"zh_CN": "组中的平均值。空值不参与计算。"
-	}
-}]
+{
+	"about": {
+		"trial":false,
+		"author": {
+			"name": "",
+			"email": "",
+			"company": "",
+			"website": ""
+		},
+		"helpUrl": {
+			"en_US": "",
+			"zh_CN": ""
+		},
+		"description": {
+			"en_US": "",
+			"zh_CN": ""
+		}
+	},
+	"functions": [{
+		"name": "",
+		"example": "",
+		"hint": {
+			"en_US": "",
+			"zh_CN": ""
+		}
+	}]
+}
 ```
 
 

+ 28 - 0
etc/functions/accumulateWordCount.json

@@ -0,0 +1,28 @@
+{
+	"about": {
+		"trial": false,
+		"author": {
+			"name": "EMQ",
+			"email": "contact@emqx.io",
+			"company": "EMQ Technologies Co., Ltd",
+			"website": "https://www.emqx.io"
+		},
+		"helpUrl": {
+			"en_US": "",
+			"zh_CN": ""
+		},
+		"description": {
+			"en_US": "",
+			"zh_CN": ""
+		}
+	},
+	"functions": [{
+		"name": "accumulateWordCount",
+		"example": "accumulateWordCount(col1)",
+		"hint": {
+			"en_US": "A function which will count how many words had been received from the beginning",
+			"zh_CN": "该函数将计算从一开始就收到了多少个单词"
+		}
+	}]
+}
+

+ 28 - 0
etc/functions/countPlusOne.json

@@ -0,0 +1,28 @@
+{
+	"about": {
+		"trial": false,
+		"author": {
+			"name": "EMQ",
+			"email": "contact@emqx.io",
+			"company": "EMQ Technologies Co., Ltd",
+			"website": "https://www.emqx.io"
+		},
+		"helpUrl": {
+			"en_US": "",
+			"zh_CN": ""
+		},
+		"description": {
+			"en_US": "",
+			"zh_CN": ""
+		}
+	},
+	"functions": [{
+		"name": "countPlusOne",
+		"example": "countPlusOne(col1)",
+		"hint": {
+			"en_US": "",
+			"zh_CN": "输出参数长度加一"
+		}
+	}]
+}
+

+ 27 - 0
etc/functions/echo.json

@@ -0,0 +1,27 @@
+{
+	"about": {
+		"trial": false,
+		"author": {
+			"name": "EMQ",
+			"email": "contact@emqx.io",
+			"company": "EMQ Technologies Co., Ltd",
+			"website": "https://www.emqx.io"
+		},
+		"helpUrl": {
+			"en_US": "",
+			"zh_CN": ""
+		},
+		"description": {
+			"en_US": "",
+			"zh_CN": ""
+		}
+	},
+	"functions": [{
+		"name": "echo",
+		"example": "echo(col1)",
+		"hint": {
+			"en_US": "",
+			"zh_CN": "原样输出参数值。"
+		}
+	}]
+}

+ 489 - 470
etc/functions/internal.json

@@ -1,470 +1,489 @@
-[{
-	"name": "avg",
-	"example": "avg(col1)",
-	"hint": {
-		"en_US": "The average of the values in a group. The null values will be ignored.",
-		"zh_CN": "组中的平均值。空值不参与计算。"
-	}
-}, {
-	"name": "count",
-	"example": "count(*)",
-	"hint": {
-		"en_US": "The number of items in a group. The null values will be ignored.",
-		"zh_CN": "组中的项目数。空值不参与计算。"
-	}
-}, {
-	"name": "max",
-	"example": "max(col1)",
-	"hint": {
-		"en_US": "The maximum value in a group. The null values will be ignored.",
-		"zh_CN": "组中的最大值。空值不参与计算。"
-	}
-}, {
-	"name": "min",
-	"example": "min(col1)",
-	"hint": {
-		"en_US": "The minimum value in a group. The null values will be ignored.",
-		"zh_CN": "组中的最小值。空值不参与计算。"
-	}
-}, {
-	"name": "sum",
-	"example": "sum(col1)",
-	"hint": {
-		"en_US": "The sum of all the values in a group. The null values will be ignored.",
-		"zh_CN": "组中所有值的总和。空值不参与计算。"
-	}
-}, {
-	"name": "collect",
-	"example": "collect(*), collect(col1)",
-	"hint": {
-		"en_US": "Returns an array with all column or the whole record (when the parameter is *) values from the group.",
-		"zh_CN": "返回组中指定的列或整个消息(参数为*时)的值组成的数组。"
-	}
-}, {
-	"name": "abs",
-	"example": "abs(col1)",
-	"hint": {
-		"en_US": "The absolute value of a value",
-		"zh_CN": "绝对值"
-	}
-}, {
-	"name": "acos",
-	"example": "acos(col1)",
-	"hint": {
-		"en_US": "The inverse cosine of a number in radians",
-		"zh_CN": "弧度数的反余弦值"
-	}
-}, {
-	"name": "asin",
-	"example": "asin(col1)",
-	"hint": {
-		"en_US": "The inverse sine of a number in radians",
-		"zh_CN": "弧度数的反正弦值"
-	}
-}, {
-	"name": "atan",
-	"example": "atan(col1)",
-	"hint": {
-		"en_US": "The inverse tangent of a number in radians",
-		"zh_CN": "弧度数的反正切值"
-	}
-}, {
-	"name": "atan2",
-	"example": "atan2(col1, col2)",
-	"hint": {
-		"en_US": "The angle, in radians, between the positive x-axis and the (x, y) point defined in the two arguments",
-		"zh_CN": "正x轴与两个自变量中定义的(x,y)点之间的弧度角"
-	}
-}, {
-	"name": "bitand",
-	"example": "bitand(col1, col2)",
-	"hint": {
-		"en_US": "Performs a bitwise AND on the bit representations of the two Int(-converted) arguments",
-		"zh_CN": "对两个Int(-converted)参数的位表示执行按位“与”运算"
-	}
-}, {
-	"name": "bitor",
-	"example": "bitor(col1, col2)",
-	"hint": {
-		"en_US": "Performs a bitwise OR of the bit representations of the two arguments",
-		"zh_CN": "对两个参数的位表示进行或运算"
-	}
-}, {
-	"name": "bitxor",
-	"example": "bitxor(col1, col2)",
-	"hint": {
-		"en_US": "Performs a bitwise XOR on the bit representations of the two Int(-converted) arguments",
-		"zh_CN": "对两个Int(-converted)参数的位表示执行逐位异或运算"
-	}
-}, {
-	"name": "bitnot",
-	"example": "bitnot(col1)",
-	"hint": {
-		"en_US": "Performs a bitwise NOT on the bit representations of the Int(-converted) argument",
-		"zh_CN": "在Int(-converted)参数的位表示形式上执行按位NOT运算"
-	}
-}, {
-	"name": "ceil",
-	"example": "ceil(col1)",
-	"hint": {
-		"en_US": "Round a value up to the nearest BIGINT value.",
-		"zh_CN": "将值舍入到最接近的BIGINT值。"
-	}
-}, {
-	"name": "cos",
-	"example": "cos(col1)",
-	"hint": {
-		"en_US": "Returns the cosine of a number in radians.",
-		"zh_CN": "返回以弧度为单位的数字的余弦值。"
-	}
-}, {
-	"name": "cosh",
-	"example": "cosh(col1)",
-	"hint": {
-		"en_US": "Returns the hyperbolic cosine of a number in radians.",
-		"zh_CN": "返回弧度数的双曲余弦值。"
-	}
-}, {
-	"name": "exp",
-	"example": "exp(col1)",
-	"hint": {
-		"en_US": "Returns e raised to the Decimal argument.",
-		"zh_CN": "返回小数点参数的e。"
-	}
-}, {
-	"name": "ln",
-	"example": "ln(col1)",
-	"hint": {
-		"en_US": "Returns the natural logarithm of the argument.",
-		"zh_CN": "返回参数的自然对数。"
-	}
-}, {
-	"name": "log",
-	"example": "log(col1)",
-	"hint": {
-		"en_US": "Returns the base 10 logarithm of the argument.",
-		"zh_CN": "返回参数的以10为底的对数。"
-	}
-}, {
-	"name": "mod",
-	"example": "mod(col1, col2)",
-	"hint": {
-		"en_US": "Returns the remainder of the division of the first argument by the second argument.",
-		"zh_CN": "返回第一个参数除以第二个参数的余数。"
-	}
-}, {
-	"name": "power",
-	"example": "power(x, y)",
-	"hint": {
-		"en_US": "Pow returns x**y, the base-x exponential of y.",
-		"zh_CN": "返回 x 的 y 次方。"
-	}
-}, {
-	"name": "rand",
-	"example": "rand()",
-	"hint": {
-		"en_US": "Returns a pseudorandom, uniformly distributed double between 0.0 and 1.0.",
-		"zh_CN": "返回一个伪随机数,其均匀分布在0.0和1.0之间。"
-	}
-}, {
-	"name": "round",
-	"example": "round(col1)",
-	"hint": {
-		"en_US": "Round a value to the nearest BIGINT value.",
-		"zh_CN": "将值四舍五入到最接近的 BIGINT 值。"
-	}
-}, {
-	"name": "sign",
-	"example": "sign(col1)",
-	"hint": {
-		"en_US": "Returns the sign of the given number. When the sign of the argument is positive, 1 is returned. When the sign of the argument is negative, -1 is returned. If the argument is 0, 0 is returned.",
-		"zh_CN": "返回给定数字的符号。 当参数的符号为正时,将返回1。 当参数的符号为负数时,返回-1。 如果参数为0,则返回0。"
-	}
-}, {
-	"name": "sin",
-	"example": "sin(col1)",
-	"hint": {
-		"en_US": "Returns the sine of a number in radians.",
-		"zh_CN": "返回弧度数的正弦值。"
-	}
-}, {
-	"name": "sinh",
-	"example": "sinh(col1)",
-	"hint": {
-		"en_US": "Returns the hyperbolic sine of a number in radians.",
-		"zh_CN": "返回弧度数的双曲正弦值。"
-	}
-}, {
-	"name": "sqrt",
-	"example": "sqrt(col1)",
-	"hint": {
-		"en_US": "Returns the square root of a number.",
-		"zh_CN": "返回数字的平方根。"
-	}
-}, {
-	"name": "tan",
-	"example": "tan(col1)",
-	"hint": {
-		"en_US": "Returns the tangent of a number in radians.",
-		"zh_CN": "返回以弧度表示的数字的正切值。"
-	}
-}, {
-	"name": "tanh",
-	"example": "tanh(col1)",
-	"hint": {
-		"en_US": "Returns the hyperbolic tangent of a number in radians.",
-		"zh_CN": "返回弧度数的双曲正切值。"
-	}
-}, {
-	"name": "concat",
-	"example": "concat(col1...)",
-	"hint": {
-		"en_US": "Concatenates arrays or strings. This function accepts any number of arguments and returns a String or an Array",
-		"zh_CN": "连接数组或字符串。 此函数接受任意数量的参数并返回 String 或 Array"
-	}
-}, {
-	"name": "endswith",
-	"example": "endswith(col1, col2)",
-	"hint": {
-		"en_US": "Returns a Boolean indicating whether the first String argument ends with the second String argument.",
-		"zh_CN": "返回一个布尔值,该布尔值指示第一个 String参数是否以第二个 String 参数结尾。"
-	}
-}, {
-	"name": "format_time",
-	"example": "format_time(col1, format)",
-	"hint": {
-		"en_US": "Format a datetime to string.",
-		"zh_CN": "将日期时间格式化为字符串。"
-	}
-}, {
-	"name": "indexof",
-	"example": "indexof(col1, col2)",
-	"hint": {
-		"en_US": "Returns the first index (0-based) of the second argument as a substring in the first argument.",
-		"zh_CN": "返回第二个参数的第一个索引(从0开始),作为第一个参数中的子字符串。"
-	}
-}, {
-	"name": "length",
-	"example": "length(col1)",
-	"hint": {
-		"en_US": "Returns the number of characters in the provided string.",
-		"zh_CN": "返回提供的字符串中的字符数。"
-	}
-}, {
-	"name": "lower",
-	"example": "lower(col1)",
-	"hint": {
-		"en_US": "Returns the lowercase version of the given String.",
-		"zh_CN": "返回给定 String 的小写版本。"
-	}
-}, {
-	"name": "lpad",
-	"example": "lpad(col1, 2)",
-	"hint": {
-		"en_US": "Returns the String argument, padded on the left side with the number of spaces specified by the second argument.",
-		"zh_CN": "返回 String,在左侧用第二个参数指定的空格数填充。"
-	}
-}, {
-	"name": "ltrim",
-	"example": "ltrim(col1)",
-	"hint": {
-		"en_US": "Removes all leading whitespace (tabs and spaces) from the provided String.",
-		"zh_CN": "从提供的字符串中删除所有前导空格(制表符和空格)。"
-	}
-}, {
-	"name": "numbytes",
-	"example": "numbytes(col1)",
-	"hint": {
-		"en_US": "Returns the number of bytes in the UTF-8 encoding of the provided string.",
-		"zh_CN": "以提供的字符串的 UTF-8 编码返回字节数。"
-	}
-}, {
-	"name": "regexp_matches",
-	"example": "regexp_matches(col1, regex)",
-	"hint": {
-		"en_US": "Returns true if the string (first argument) contains a match for the regular expression.",
-		"zh_CN": "如果字符串(第一个参数)包含正则表达式的匹配项,则返回 true。"
-	}
-}, {
-	"name": "regexp_replace",
-	"example": "regexp_matches(col1, regex, str)",
-	"hint": {
-		"en_US": "Replaces all occurrences of the second argument (regular expression) in the first argument with the third argument.",
-		"zh_CN": "将第一个参数中所有出现的第二个参数(正则表达式)替换为第三个参数。"
-	}
-}, {
-	"name": "regexp_substr",
-	"example": "regexp_substr(col1, regex)",
-	"hint": {
-		"en_US": "Finds the first match of the 2nd parameter (regex) in the first parameter.",
-		"zh_CN": "在第一个参数中找到第二个参数(regex)的第一个匹配项。"
-	}
-}, {
-	"name": "rpad",
-	"example": "rpad(col1, 2)",
-	"hint": {
-		"en_US": "Returns the String argument, padded on the right side with the number of spaces specified by the second argument.",
-		"zh_CN": "返回字符串参数,在右侧填充第二个参数指定的空格数。"
-	}
-}, {
-	"name": "rtrim",
-	"example": "rtrim(col1)",
-	"hint": {
-		"en_US": "Removes all trailing whitespace (tabs and spaces) from the provided String.",
-		"zh_CN": "从提供的字符串中删除所有尾随空白(制表符和空格)。"
-	}
-}, {
-	"name": "substring",
-	"example": "substring(col1, start, end)",
-	"hint": {
-		"en_US": "returns the substring of the provided String from the provided Int index (0-based, inclusive) to the end of the String.",
-		"zh_CN": "从提供的 Int 索引(从0开始,包括0)到字符串的结尾,返回提供的String的子字符串。"
-	}
-}, {
-	"name": "startswith",
-	"example": "startswith(col1, str)",
-	"hint": {
-		"en_US": "Returns Boolean, whether the first string argument starts with the second string argument.",
-		"zh_CN": "返回布尔值,是否第一个字符串参数是否以第二个字符串参数开头。"
-	}
-}, {
-	"name": "split_value",
-	"example": "split_value(col1, str_splitter, index)",
-	"hint": {
-		"en_US": "Splitthevalueofthe1stparameterwiththe2ndparameter,andreturnthevalueofsplitarraythatindexedwiththe3rdparameter.split_value(\"/test/device001/message\",\"/\",0)ASa,thereturnedvalueoffunctionisempty;split_value(\"/test/device001/message\",\"/\",3)ASa,thereturnedvalueoffunctionismessage;",
-		"zh_CN": "用第二个参数分割第一个参数的值,并返回用第三个参数索引的分割数组的值。split_value(“/test/device001/message”,“/”,0)ASa,函数的返回值为空;split_value(“/test/device001/message”,“/”,3)ASa,函数的返回值为message。"
-	}
-}, {
-	"name": "trim",
-	"example": "trim(col1)",
-	"hint": {
-		"en_US": "Removes all leading and trailing whitespace (tabs and spaces) from the provided String.",
-		"zh_CN": "从提供的字符串中删除所有前导和尾随空格(制表符和空格)。"
-	}
-}, {
-	"name": "upper",
-	"example": "upper(col1)",
-	"hint": {
-		"en_US": "Returns the uppercase version of the given String.",
-		"zh_CN": "返回给定 String 的大写版本。"
-	}
-}, {
-	"name": "cast",
-	"example": "cast(col, \"bigint\")",
-	"hint": {
-		"en_US": "Converts a value from one data type to another. The supported types includes: bigint, float, string, boolean and datetime(not supported now).",
-		"zh_CN": "将值从一种数据类型转换为另一种数据类型。 支持的类型包括:bigint,float,string,boolean 和 datetime(现在不支持)。"
-	}
-}, {
-	"name": "chr",
-	"example": "chr(col1)",
-	"hint": {
-		"en_US": "Returns the ASCII character that corresponds to the given Int argument",
-		"zh_CN": "返回与给定 Int 参数对应的 ASCII 字符"
-	}
-}, {
-	"name": "encode",
-	"example": "encode(col1, \"base64\")",
-	"hint": {
-		"en_US": "Use the encode function to encode the payload, which potentially might be non-JSON data, into its string representation based on the encoding scheme. Currently, only \"base64\" econding type is supported.",
-		"zh_CN": "使用 encode 函数根据编码方案将负载(可能是非 JSON 数据)编码为其字符串表示形式。目前,只支持\"base64\"econding 类型。"
-	}
-}, {
-	"name": "trunc",
-	"example": "trunc(dec, int)",
-	"hint": {
-		"en_US": "iTruncates the first argument to the number of Decimal places specified by the second argument. If the second argument is less than zero, it is set to zero. If the second argument is greater than 34, it is set to 34. Trailing zeroes are stripped from the result.",
-		"zh_CN": "将第一个参数截断为第二个参数指定的小数位数。 如果第二个参数小于零,则将其设置为零。 如果第二个参数大于34,则将其设置为34。从结果中去除尾随零。"
-	}
-}, {
-	"name": "md5",
-	"example": "md5(col1)",
-	"hint": {
-		"en_US": "Hashed value of the argument",
-		"zh_CN": "参数的哈希值"
-	}
-}, {
-	"name": "sha1",
-	"example": "sha1(col1)",
-	"hint": {
-		"en_US": "Hashed value of the argument",
-		"zh_CN": "参数的哈希值"
-	}
-}, {
-	"name": "sha256",
-	"example": "sha256(col1)",
-	"hint": {
-		"en_US": "Hashed value of the argument",
-		"zh_CN": "参数的哈希值"
-	}
-}, {
-	"name": "sha384",
-	"example": "sha384(col1)",
-	"hint": {
-		"en_US": "Hashed value of the argument",
-		"zh_CN": "参数的哈希值"
-	}
-}, {
-	"name": "sha512",
-	"example": "sha512(col1)",
-	"hint": {
-		"en_US": "Hashed value of the argument",
-		"zh_CN": "参数的哈希值"
-	}
-}, {
-	"name": "json_path_exists",
-	"example": "json_path_exists(col1, \"$.name\")",
-	"hint": {
-		"en_US": "Checks whether JSON path returns any item for the specified JSON value. Return bool value.",
-		"zh_CN": "检查 JSON 路径是否返回指定JSON 值的任何项目。 返回布尔值。"
-	}
-}, {
-	"name": "json_path_query",
-	"example": "json_path_query(col1, \"$.name\")",
-	"hint": {
-		"en_US": "Gets all items returned by JSON path for the specified JSON value.",
-		"zh_CN": "获取 JSON 路径返回的指定 JSON值的所有项目。"
-	}
-}, {
-	"name": "json_path_query_first",
-	"example": "json_path_query_first(col1, \"$.name\")",
-	"hint": {
-		"en_US": "Gets the first item returned by JSON path for the specified JSON value.",
-		"zh_CN": "获取 JSON 路径返回的指定 JSON值的第一项。"
-	}
-}, {
-	"name": "isNull",
-	"example": "isNull(col1)",
-	"hint": {
-		"en_US": "Returns true if the argument is the Null value.",
-		"zh_CN": "如果参数为空值,则返回 true。"
-	}
-}, {
-	"name": "newuuid",
-	"example": "newuuid()",
-	"hint": {
-		"en_US": "Returns a random 16-byte UUID.",
-		"zh_CN": "返回一个随机的16字节 UUID。"
-	}
-}, {
-	"name": "tstamp",
-	"example": "tstamp()",
-	"hint": {
-		"en_US": "Returns the current timestamp in milliseconds from 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970",
-		"zh_CN": "返回当前时间戳,以1970年1月1日星期四00:00:00协调世界时(UTC)为单位。"
-	}
-}, {
-	"name": "mqtt",
-	"example": "mqtt(topic)",
-	"hint": {
-		"en_US": "Returns the MQTT meta-data of specified key. The current supported keys - topic: return the topic of message. If there are multiple stream source, then specify the source name in parameter. Such as mqtt(src1.topic) - messageid: return the message id of message. If there are multiple stream source, then specify the source name in parameter. Such as mqtt(src2.messageid)",
-		"zh_CN": "返回指定键的 MQTT 元数据。 当前支持的键包括 -topic:返回消息的主题。 如果有多个流源,则在参数中指定源名称。 如 mqtt(src1.topic) - messageid:返回消息的消息ID。 如果有多个流源,则在参数中指定源名称。 如 mqtt(src2.messageid)"
-	}
-}, {
-	"name": "meta",
-	"example": "meta(topic)",
-	"hint": {
-		"en_US": "Returns the meta-data of specified key. The key could be: - a standalone key if there is only one source in the from clause, such as meta(device) - A qualified key to specify the stream, such as meta(src1.device) - A key with arrow for multi level meta data, such as meta(src1.reading->device->name) This assumes reading is a map structure meta data.",
-		"zh_CN": "返回指定键的元数据。 键可能是:-如果 from 子句中只有一个来源,则为独立键,例如meta(device) -用于指定流的合格键,例如 meta(src1.device) -用于多级元数据的带有箭头的键,例如 meta(src1.reading->device->name)。这里假定读取是地图结构元数据。"
-	}
-}]
+{
+	"about": {
+		"trial": false,
+		"author": {
+			"name": "EMQ",
+			"email": "contact@emqx.io",
+			"company": "EMQ Technologies Co., Ltd",
+			"website": "https://www.emqx.io"
+		},
+		"helpUrl": {
+			"en_US": "https://github.com/emqx/kuiper/blob/master/docs/en_US/sqls/built-in_functions.md",
+			"zh_CN": "https://github.com/emqx/kuiper/blob/master/docs/zh_CN/sqls/built-in_functions.md"
+		},
+		"description": {
+			"en_US": "Kuiper has many built-in functions for performing calculations on data.",
+			"zh_CN": "Kuiper 具有许多内置函数,可以对数据执行计算。"
+		}
+	},
+	"functions": [{
+		"name": "avg",
+		"example": "avg(col1)",
+		"hint": {
+			"en_US": "The average of the values in a group. The null values will be ignored.",
+			"zh_CN": "组中的平均值。空值不参与计算。"
+		}
+	}, {
+		"name": "count",
+		"example": "count(*)",
+		"hint": {
+			"en_US": "The number of items in a group. The null values will be ignored.",
+			"zh_CN": "组中的项目数。空值不参与计算。"
+		}
+	}, {
+		"name": "max",
+		"example": "max(col1)",
+		"hint": {
+			"en_US": "The maximum value in a group. The null values will be ignored.",
+			"zh_CN": "组中的最大值。空值不参与计算。"
+		}
+	}, {
+		"name": "min",
+		"example": "min(col1)",
+		"hint": {
+			"en_US": "The minimum value in a group. The null values will be ignored.",
+			"zh_CN": "组中的最小值。空值不参与计算。"
+		}
+	}, {
+		"name": "sum",
+		"example": "sum(col1)",
+		"hint": {
+			"en_US": "The sum of all the values in a group. The null values will be ignored.",
+			"zh_CN": "组中所有值的总和。空值不参与计算。"
+		}
+	}, {
+		"name": "collect",
+		"example": "collect(*), collect(col1)",
+		"hint": {
+			"en_US": "Returns an array with all column or the whole record (when the parameter is *) values from the group.",
+			"zh_CN": "返回组中指定的列或整个消息(参数为*时)的值组成的数组。"
+		}
+	}, {
+		"name": "abs",
+		"example": "abs(col1)",
+		"hint": {
+			"en_US": "The absolute value of a value",
+			"zh_CN": "绝对值"
+		}
+	}, {
+		"name": "acos",
+		"example": "acos(col1)",
+		"hint": {
+			"en_US": "The inverse cosine of a number in radians",
+			"zh_CN": "弧度数的反余弦值"
+		}
+	}, {
+		"name": "asin",
+		"example": "asin(col1)",
+		"hint": {
+			"en_US": "The inverse sine of a number in radians",
+			"zh_CN": "弧度数的反正弦值"
+		}
+	}, {
+		"name": "atan",
+		"example": "atan(col1)",
+		"hint": {
+			"en_US": "The inverse tangent of a number in radians",
+			"zh_CN": "弧度数的反正切值"
+		}
+	}, {
+		"name": "atan2",
+		"example": "atan2(col1, col2)",
+		"hint": {
+			"en_US": "The angle, in radians, between the positive x-axis and the (x, y) point defined in the two arguments",
+			"zh_CN": "正x轴与两个自变量中定义的(x,y)点之间的弧度角"
+		}
+	}, {
+		"name": "bitand",
+		"example": "bitand(col1, col2)",
+		"hint": {
+			"en_US": "Performs a bitwise AND on the bit representations of the two Int(-converted) arguments",
+			"zh_CN": "对两个Int(-converted)参数的位表示执行按位“与”运算"
+		}
+	}, {
+		"name": "bitor",
+		"example": "bitor(col1, col2)",
+		"hint": {
+			"en_US": "Performs a bitwise OR of the bit representations of the two arguments",
+			"zh_CN": "对两个参数的位表示进行或运算"
+		}
+	}, {
+		"name": "bitxor",
+		"example": "bitxor(col1, col2)",
+		"hint": {
+			"en_US": "Performs a bitwise XOR on the bit representations of the two Int(-converted) arguments",
+			"zh_CN": "对两个Int(-converted)参数的位表示执行逐位异或运算"
+		}
+	}, {
+		"name": "bitnot",
+		"example": "bitnot(col1)",
+		"hint": {
+			"en_US": "Performs a bitwise NOT on the bit representations of the Int(-converted) argument",
+			"zh_CN": "在Int(-converted)参数的位表示形式上执行按位NOT运算"
+		}
+	}, {
+		"name": "ceil",
+		"example": "ceil(col1)",
+		"hint": {
+			"en_US": "Round a value up to the nearest BIGINT value.",
+			"zh_CN": "将值舍入到最接近的BIGINT值。"
+		}
+	}, {
+		"name": "cos",
+		"example": "cos(col1)",
+		"hint": {
+			"en_US": "Returns the cosine of a number in radians.",
+			"zh_CN": "返回以弧度为单位的数字的余弦值。"
+		}
+	}, {
+		"name": "cosh",
+		"example": "cosh(col1)",
+		"hint": {
+			"en_US": "Returns the hyperbolic cosine of a number in radians.",
+			"zh_CN": "返回弧度数的双曲余弦值。"
+		}
+	}, {
+		"name": "exp",
+		"example": "exp(col1)",
+		"hint": {
+			"en_US": "Returns e raised to the Decimal argument.",
+			"zh_CN": "返回小数点参数的e。"
+		}
+	}, {
+		"name": "ln",
+		"example": "ln(col1)",
+		"hint": {
+			"en_US": "Returns the natural logarithm of the argument.",
+			"zh_CN": "返回参数的自然对数。"
+		}
+	}, {
+		"name": "log",
+		"example": "log(col1)",
+		"hint": {
+			"en_US": "Returns the base 10 logarithm of the argument.",
+			"zh_CN": "返回参数的以10为底的对数。"
+		}
+	}, {
+		"name": "mod",
+		"example": "mod(col1, col2)",
+		"hint": {
+			"en_US": "Returns the remainder of the division of the first argument by the second argument.",
+			"zh_CN": "返回第一个参数除以第二个参数的余数。"
+		}
+	}, {
+		"name": "power",
+		"example": "power(x, y)",
+		"hint": {
+			"en_US": "Pow returns x**y, the base-x exponential of y.",
+			"zh_CN": "返回 x 的 y 次方。"
+		}
+	}, {
+		"name": "rand",
+		"example": "rand()",
+		"hint": {
+			"en_US": "Returns a pseudorandom, uniformly distributed double between 0.0 and 1.0.",
+			"zh_CN": "返回一个伪随机数,其均匀分布在0.0和1.0之间。"
+		}
+	}, {
+		"name": "round",
+		"example": "round(col1)",
+		"hint": {
+			"en_US": "Round a value to the nearest BIGINT value.",
+			"zh_CN": "将值四舍五入到最接近的 BIGINT 值。"
+		}
+	}, {
+		"name": "sign",
+		"example": "sign(col1)",
+		"hint": {
+			"en_US": "Returns the sign of the given number. When the sign of the argument is positive, 1 is returned. When the sign of the argument is negative, -1 is returned. If the argument is 0, 0 is returned.",
+			"zh_CN": "返回给定数字的符号。 当参数的符号为正时,将返回1。 当参数的符号为负数时,返回-1。 如果参数为0,则返回0。"
+		}
+	}, {
+		"name": "sin",
+		"example": "sin(col1)",
+		"hint": {
+			"en_US": "Returns the sine of a number in radians.",
+			"zh_CN": "返回弧度数的正弦值。"
+		}
+	}, {
+		"name": "sinh",
+		"example": "sinh(col1)",
+		"hint": {
+			"en_US": "Returns the hyperbolic sine of a number in radians.",
+			"zh_CN": "返回弧度数的双曲正弦值。"
+		}
+	}, {
+		"name": "sqrt",
+		"example": "sqrt(col1)",
+		"hint": {
+			"en_US": "Returns the square root of a number.",
+			"zh_CN": "返回数字的平方根。"
+		}
+	}, {
+		"name": "tan",
+		"example": "tan(col1)",
+		"hint": {
+			"en_US": "Returns the tangent of a number in radians.",
+			"zh_CN": "返回以弧度表示的数字的正切值。"
+		}
+	}, {
+		"name": "tanh",
+		"example": "tanh(col1)",
+		"hint": {
+			"en_US": "Returns the hyperbolic tangent of a number in radians.",
+			"zh_CN": "返回弧度数的双曲正切值。"
+		}
+	}, {
+		"name": "concat",
+		"example": "concat(col1...)",
+		"hint": {
+			"en_US": "Concatenates arrays or strings. This function accepts any number of arguments and returns a String or an Array",
+			"zh_CN": "连接数组或字符串。 此函数接受任意数量的参数并返回 String 或 Array"
+		}
+	}, {
+		"name": "endswith",
+		"example": "endswith(col1, col2)",
+		"hint": {
+			"en_US": "Returns a Boolean indicating whether the first String argument ends with the second String argument.",
+			"zh_CN": "返回一个布尔值,该布尔值指示第一个 String参数是否以第二个 String 参数结尾。"
+		}
+	}, {
+		"name": "format_time",
+		"example": "format_time(col1, format)",
+		"hint": {
+			"en_US": "Format a datetime to string.",
+			"zh_CN": "将日期时间格式化为字符串。"
+		}
+	}, {
+		"name": "indexof",
+		"example": "indexof(col1, col2)",
+		"hint": {
+			"en_US": "Returns the first index (0-based) of the second argument as a substring in the first argument.",
+			"zh_CN": "返回第二个参数的第一个索引(从0开始),作为第一个参数中的子字符串。"
+		}
+	}, {
+		"name": "length",
+		"example": "length(col1)",
+		"hint": {
+			"en_US": "Returns the number of characters in the provided string.",
+			"zh_CN": "返回提供的字符串中的字符数。"
+		}
+	}, {
+		"name": "lower",
+		"example": "lower(col1)",
+		"hint": {
+			"en_US": "Returns the lowercase version of the given String.",
+			"zh_CN": "返回给定 String 的小写版本。"
+		}
+	}, {
+		"name": "lpad",
+		"example": "lpad(col1, 2)",
+		"hint": {
+			"en_US": "Returns the String argument, padded on the left side with the number of spaces specified by the second argument.",
+			"zh_CN": "返回 String,在左侧用第二个参数指定的空格数填充。"
+		}
+	}, {
+		"name": "ltrim",
+		"example": "ltrim(col1)",
+		"hint": {
+			"en_US": "Removes all leading whitespace (tabs and spaces) from the provided String.",
+			"zh_CN": "从提供的字符串中删除所有前导空格(制表符和空格)。"
+		}
+	}, {
+		"name": "numbytes",
+		"example": "numbytes(col1)",
+		"hint": {
+			"en_US": "Returns the number of bytes in the UTF-8 encoding of the provided string.",
+			"zh_CN": "以提供的字符串的 UTF-8 编码返回字节数。"
+		}
+	}, {
+		"name": "regexp_matches",
+		"example": "regexp_matches(col1, regex)",
+		"hint": {
+			"en_US": "Returns true if the string (first argument) contains a match for the regular expression.",
+			"zh_CN": "如果字符串(第一个参数)包含正则表达式的匹配项,则返回 true。"
+		}
+	}, {
+		"name": "regexp_replace",
+		"example": "regexp_matches(col1, regex, str)",
+		"hint": {
+			"en_US": "Replaces all occurrences of the second argument (regular expression) in the first argument with the third argument.",
+			"zh_CN": "将第一个参数中所有出现的第二个参数(正则表达式)替换为第三个参数。"
+		}
+	}, {
+		"name": "regexp_substr",
+		"example": "regexp_substr(col1, regex)",
+		"hint": {
+			"en_US": "Finds the first match of the 2nd parameter (regex) in the first parameter.",
+			"zh_CN": "在第一个参数中找到第二个参数(regex)的第一个匹配项。"
+		}
+	}, {
+		"name": "rpad",
+		"example": "rpad(col1, 2)",
+		"hint": {
+			"en_US": "Returns the String argument, padded on the right side with the number of spaces specified by the second argument.",
+			"zh_CN": "返回字符串参数,在右侧填充第二个参数指定的空格数。"
+		}
+	}, {
+		"name": "rtrim",
+		"example": "rtrim(col1)",
+		"hint": {
+			"en_US": "Removes all trailing whitespace (tabs and spaces) from the provided String.",
+			"zh_CN": "从提供的字符串中删除所有尾随空白(制表符和空格)。"
+		}
+	}, {
+		"name": "substring",
+		"example": "substring(col1, start, end)",
+		"hint": {
+			"en_US": "returns the substring of the provided String from the provided Int index (0-based, inclusive) to the end of the String.",
+			"zh_CN": "从提供的 Int 索引(从0开始,包括0)到字符串的结尾,返回提供的String的子字符串。"
+		}
+	}, {
+		"name": "startswith",
+		"example": "startswith(col1, str)",
+		"hint": {
+			"en_US": "Returns Boolean, whether the first string argument starts with the second string argument.",
+			"zh_CN": "返回布尔值,是否第一个字符串参数是否以第二个字符串参数开头。"
+		}
+	}, {
+		"name": "split_value",
+		"example": "split_value(col1, str_splitter, index)",
+		"hint": {
+			"en_US": "Splitthevalueofthe1stparameterwiththe2ndparameter,andreturnthevalueofsplitarraythatindexedwiththe3rdparameter.split_value(\"/test/device001/message\",\"/\",0)ASa,thereturnedvalueoffunctionisempty;split_value(\"/test/device001/message\",\"/\",3)ASa,thereturnedvalueoffunctionismessage;",
+			"zh_CN": "用第二个参数分割第一个参数的值,并返回用第三个参数索引的分割数组的值。split_value(“/test/device001/message”,“/”,0)ASa,函数的返回值为空;split_value(“/test/device001/message”,“/”,3)ASa,函数的返回值为message。"
+		}
+	}, {
+		"name": "trim",
+		"example": "trim(col1)",
+		"hint": {
+			"en_US": "Removes all leading and trailing whitespace (tabs and spaces) from the provided String.",
+			"zh_CN": "从提供的字符串中删除所有前导和尾随空格(制表符和空格)。"
+		}
+	}, {
+		"name": "upper",
+		"example": "upper(col1)",
+		"hint": {
+			"en_US": "Returns the uppercase version of the given String.",
+			"zh_CN": "返回给定 String 的大写版本。"
+		}
+	}, {
+		"name": "cast",
+		"example": "cast(col, \"bigint\")",
+		"hint": {
+			"en_US": "Converts a value from one data type to another. The supported types includes: bigint, float, string, boolean and datetime(not supported now).",
+			"zh_CN": "将值从一种数据类型转换为另一种数据类型。 支持的类型包括:bigint,float,string,boolean 和 datetime(现在不支持)。"
+		}
+	}, {
+		"name": "chr",
+		"example": "chr(col1)",
+		"hint": {
+			"en_US": "Returns the ASCII character that corresponds to the given Int argument",
+			"zh_CN": "返回与给定 Int 参数对应的 ASCII 字符"
+		}
+	}, {
+		"name": "encode",
+		"example": "encode(col1, \"base64\")",
+		"hint": {
+			"en_US": "Use the encode function to encode the payload, which potentially might be non-JSON data, into its string representation based on the encoding scheme. Currently, only \"base64\" econding type is supported.",
+			"zh_CN": "使用 encode 函数根据编码方案将负载(可能是非 JSON 数据)编码为其字符串表示形式。目前,只支持\"base64\"econding 类型。"
+		}
+	}, {
+		"name": "trunc",
+		"example": "trunc(dec, int)",
+		"hint": {
+			"en_US": "iTruncates the first argument to the number of Decimal places specified by the second argument. If the second argument is less than zero, it is set to zero. If the second argument is greater than 34, it is set to 34. Trailing zeroes are stripped from the result.",
+			"zh_CN": "将第一个参数截断为第二个参数指定的小数位数。 如果第二个参数小于零,则将其设置为零。 如果第二个参数大于34,则将其设置为34。从结果中去除尾随零。"
+		}
+	}, {
+		"name": "md5",
+		"example": "md5(col1)",
+		"hint": {
+			"en_US": "Hashed value of the argument",
+			"zh_CN": "参数的哈希值"
+		}
+	}, {
+		"name": "sha1",
+		"example": "sha1(col1)",
+		"hint": {
+			"en_US": "Hashed value of the argument",
+			"zh_CN": "参数的哈希值"
+		}
+	}, {
+		"name": "sha256",
+		"example": "sha256(col1)",
+		"hint": {
+			"en_US": "Hashed value of the argument",
+			"zh_CN": "参数的哈希值"
+		}
+	}, {
+		"name": "sha384",
+		"example": "sha384(col1)",
+		"hint": {
+			"en_US": "Hashed value of the argument",
+			"zh_CN": "参数的哈希值"
+		}
+	}, {
+		"name": "sha512",
+		"example": "sha512(col1)",
+		"hint": {
+			"en_US": "Hashed value of the argument",
+			"zh_CN": "参数的哈希值"
+		}
+	}, {
+		"name": "json_path_exists",
+		"example": "json_path_exists(col1, \"$.name\")",
+		"hint": {
+			"en_US": "Checks whether JSON path returns any item for the specified JSON value. Return bool value.",
+			"zh_CN": "检查 JSON 路径是否返回指定JSON 值的任何项目。 返回布尔值。"
+		}
+	}, {
+		"name": "json_path_query",
+		"example": "json_path_query(col1, \"$.name\")",
+		"hint": {
+			"en_US": "Gets all items returned by JSON path for the specified JSON value.",
+			"zh_CN": "获取 JSON 路径返回的指定 JSON值的所有项目。"
+		}
+	}, {
+		"name": "json_path_query_first",
+		"example": "json_path_query_first(col1, \"$.name\")",
+		"hint": {
+			"en_US": "Gets the first item returned by JSON path for the specified JSON value.",
+			"zh_CN": "获取 JSON 路径返回的指定 JSON值的第一项。"
+		}
+	}, {
+		"name": "isNull",
+		"example": "isNull(col1)",
+		"hint": {
+			"en_US": "Returns true if the argument is the Null value.",
+			"zh_CN": "如果参数为空值,则返回 true。"
+		}
+	}, {
+		"name": "newuuid",
+		"example": "newuuid()",
+		"hint": {
+			"en_US": "Returns a random 16-byte UUID.",
+			"zh_CN": "返回一个随机的16字节 UUID。"
+		}
+	}, {
+		"name": "tstamp",
+		"example": "tstamp()",
+		"hint": {
+			"en_US": "Returns the current timestamp in milliseconds from 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970",
+			"zh_CN": "返回当前时间戳,以1970年1月1日星期四00:00:00协调世界时(UTC)为单位。"
+		}
+	}, {
+		"name": "mqtt",
+		"example": "mqtt(topic)",
+		"hint": {
+			"en_US": "Returns the MQTT meta-data of specified key. The current supported keys - topic: return the topic of message. If there are multiple stream source, then specify the source name in parameter. Such as mqtt(src1.topic) - messageid: return the message id of message. If there are multiple stream source, then specify the source name in parameter. Such as mqtt(src2.messageid)",
+			"zh_CN": "返回指定键的 MQTT 元数据。 当前支持的键包括 -topic:返回消息的主题。 如果有多个流源,则在参数中指定源名称。 如 mqtt(src1.topic) - messageid:返回消息的消息ID。 如果有多个流源,则在参数中指定源名称。 如 mqtt(src2.messageid)"
+		}
+	}, {
+		"name": "meta",
+		"example": "meta(topic)",
+		"hint": {
+			"en_US": "Returns the meta-data of specified key. The key could be: - a standalone key if there is only one source in the from clause, such as meta(device) - A qualified key to specify the stream, such as meta(src1.device) - A key with arrow for multi level meta data, such as meta(src1.reading->device->name) This assumes reading is a map structure meta data.",
+			"zh_CN": "返回指定键的元数据。 键可能是:-如果 from 子句中只有一个来源,则为独立键,例如meta(device) -用于指定流的合格键,例如 meta(src1.device) -用于多级元数据的带有箭头的键,例如 meta(src1.reading->device->name)。这里假定读取是地图结构元数据。"
+		}
+	}]
+}

+ 1 - 1
etc/sinks/edgex.json

@@ -330,4 +330,4 @@
       ]
     }
   ]
-}
+}

+ 40 - 24
plugins/funcMeta.go

@@ -11,33 +11,43 @@ import (
 type (
 	fileFunc struct {
 		Name    string        `json:"name"`
-		Control string        `json:"control"`
 		Example string        `json:"example"`
 		Hint    *fileLanguage `json:"hint"`
 	}
+	fileFuncs struct {
+		About   *fileAbout  `json:"about"`
+		FiFuncs []*fileFunc `json:"functions"`
+	}
 	uiFunc struct {
 		Name    string    `json:"name"`
-		Control string    `json:"control"`
 		Example string    `json:"example"`
 		Hint    *language `json:"hint"`
 	}
+	uiFuncs struct {
+		About   *about    `json:"about"`
+		UiFuncs []*uiFunc `json:"functions"`
+	}
 )
 
-func newUiFunc(fi *fileFunc) *uiFunc {
+func newUiFuncs(fi *fileFuncs) *uiFuncs {
 	if nil == fi {
 		return nil
 	}
-	ui := new(uiFunc)
-	ui.Name = fi.Name
-	ui.Control = fi.Control
-	ui.Example = fi.Example
-	ui.Hint = newLanguage(fi.Hint)
-	return ui
+	uis := new(uiFuncs)
+	uis.About = newAbout(fi.About)
+	for _, v := range fi.FiFuncs {
+		ui := new(uiFunc)
+		ui.Name = v.Name
+		ui.Example = v.Example
+		ui.Hint = newLanguage(v.Hint)
+		uis.UiFuncs = append(uis.UiFuncs, ui)
+	}
+	return uis
 }
 
-var g_funcMetadata []*uiFunc
+var g_funcMetadata []*uiFuncs
 
-func readFuncMetaDir() error {
+func (m *Manager) readFuncMetaDir() error {
 	confDir, err := common.GetConfLoc()
 	if nil != err {
 		return err
@@ -55,32 +65,38 @@ func readFuncMetaDir() error {
 		}
 
 		filePath := path.Join(dir, fname)
-		var fis []*fileFunc
-		err = common.ReadJsonUnmarshal(filePath, &fis)
+		fis := new(fileFuncs)
+		err = common.ReadJsonUnmarshal(filePath, fis)
 		if nil != err {
 			return fmt.Errorf("fname:%s err:%v", fname, err)
 		}
-		common.Log.Infof("funcMeta file : %s", fname)
-		for _, fi := range fis {
-			g_funcMetadata = append(g_funcMetadata, newUiFunc(fi))
+		if nil == fis.About {
+			return fmt.Errorf("not found about of %s", filePath)
+		} else {
+			_, fis.About.Installed = m.registry.Get(FUNCTION, strings.TrimSuffix(fname, `.json`))
 		}
+		common.Log.Infof("funcMeta file : %s", fname)
+		g_funcMetadata = append(g_funcMetadata, newUiFuncs(fis))
 	}
 	return nil
 }
 
-func readFuncMetaFile(filePath string) error {
-	var fis []*fileFunc
-	err := common.ReadJsonUnmarshal(filePath, &fis)
+func (m *Manager) readFuncMetaFile(filePath string) error {
+	fiName := path.Base(filePath)
+	fis := new(fileFuncs)
+	err := common.ReadJsonUnmarshal(filePath, fis)
 	if nil != err {
 		return fmt.Errorf("filePath:%s err:%v", filePath, err)
 	}
-	for _, fi := range fis {
-		g_funcMetadata = append(g_funcMetadata, newUiFunc(fi))
+	if nil == fis.About {
+		return fmt.Errorf("not found about of %s", filePath)
+	} else {
+		_, fis.About.Installed = m.registry.Get(FUNCTION, strings.TrimSuffix(fiName, `.json`))
 	}
-
-	common.Log.Infof("funcMeta file : %s", path.Base(filePath))
+	g_funcMetadata = append(g_funcMetadata, newUiFuncs(fis))
+	common.Log.Infof("funcMeta file : %s", fiName)
 	return nil
 }
-func GetFunctions() []*uiFunc {
+func GetFunctions() []*uiFuncs {
 	return g_funcMetadata
 }

+ 12 - 10
plugins/manager.go

@@ -212,13 +212,13 @@ func NewPluginManager() (*Manager, error) {
 			etcDir:    etcDir,
 			registry:  registry,
 		}
-		if err := readSourceMetaDir(); nil != err {
+		if err := singleton.readSourceMetaDir(); nil != err {
 			common.Log.Errorf("readSourceMetaDir:%v", err)
 		}
-		if err := readSinkMetaDir(); nil != err {
+		if err := singleton.readSinkMetaDir(); nil != err {
 			common.Log.Errorf("readSinkMetaDir:%v", err)
 		}
-		if err := readFuncMetaDir(); nil != err {
+		if err := singleton.readFuncMetaDir(); nil != err {
 			common.Log.Errorf("readFuncMetaDir:%v", err)
 		}
 	})
@@ -283,20 +283,22 @@ func (m *Manager) Register(t PluginType, j *Plugin) error {
 		}
 		return fmt.Errorf("fail to unzip file %s: %s", uri, err)
 	}
-	if SINK == t {
-		if err := readSinkMetaFile(path.Join(m.etcDir, PluginTypes[t], name+`.json`)); nil != err {
+	m.registry.Store(t, name, version)
+
+	switch t {
+	case SINK:
+		if err := m.readSinkMetaFile(path.Join(m.etcDir, PluginTypes[t], name+`.json`)); nil != err {
 			common.Log.Errorf("readSinkFile:%v", err)
 		}
-	} else if SOURCE == t {
-		if err := readSourceMetaFile(path.Join(m.etcDir, PluginTypes[t], name+`.json`)); nil != err {
+	case SOURCE:
+		if err := m.readSourceMetaFile(path.Join(m.etcDir, PluginTypes[t], name+`.json`)); nil != err {
 			common.Log.Errorf("readSourceFile:%v", err)
 		}
-	} else if FUNCTION == t {
-		if err := readFuncMetaFile(path.Join(m.etcDir, PluginTypes[t], name+`.json`)); nil != err {
+	case FUNCTION:
+		if err := m.readFuncMetaFile(path.Join(m.etcDir, PluginTypes[t], name+`.json`)); nil != err {
 			common.Log.Errorf("readFuncFile:%v", err)
 		}
 	}
-	m.registry.Store(t, name, version)
 	return nil
 }
 

+ 31 - 24
plugins/sinkMeta.go

@@ -38,6 +38,7 @@ type (
 	}
 	fileAbout struct {
 		Trial       bool          `json:"trial"`
+		Installed   bool          `json:"installed"`
 		Author      *author       `json:"author"`
 		HelpUrl     *fileLanguage `json:"helpUrl"`
 		Description *fileLanguage `json:"description"`
@@ -53,6 +54,7 @@ type (
 	}
 	about struct {
 		Trial       bool      `json:"trial"`
+		Installed   bool      `json:"installed"`
 		Author      *author   `json:"author"`
 		HelpUrl     *language `json:"helpUrl"`
 		Description *language `json:"description"`
@@ -144,14 +146,14 @@ func newUiSink(fi *fileSink) (*uiSink, error) {
 }
 
 var g_sinkMetadata map[string]*uiSink //map[fileName]
-func readSinkMetaDir() error {
+func (m *Manager) readSinkMetaDir() error {
+	g_sinkMetadata = make(map[string]*uiSink)
 	confDir, err := common.GetConfLoc()
 	if nil != err {
 		return err
 	}
 
 	dir := path.Join(confDir, "sinks")
-	tmpMap := make(map[string]*uiSink)
 	files, err := ioutil.ReadDir(dir)
 	if nil != err {
 		return err
@@ -163,42 +165,36 @@ func readSinkMetaDir() error {
 		}
 
 		filePath := path.Join(dir, fname)
-		metadata := new(fileSink)
-		err = common.ReadJsonUnmarshal(filePath, metadata)
-		if nil != err {
-			return fmt.Errorf("fname:%s err:%v", fname, err)
-		}
-		common.Log.Infof("Loading metadata file for sink: %s", fname)
-		tmpMap[fname], err = newUiSink(metadata)
-		if nil != err {
+		if err := m.readSinkMetaFile(filePath); nil != err {
 			return err
 		}
 	}
-	g_sinkMetadata = tmpMap
 	return nil
 }
 
-func readSinkMetaFile(filePath string) error {
-	ptrMetadata := new(fileSink)
-	err := common.ReadJsonUnmarshal(filePath, ptrMetadata)
+func (m *Manager) readSinkMetaFile(filePath string) error {
+	finame := path.Base(filePath)
+	pluginName := strings.TrimSuffix(finame, `.json`)
+	metadata := new(fileSink)
+	err := common.ReadJsonUnmarshal(filePath, metadata)
 	if nil != err {
 		return fmt.Errorf("filePath:%s err:%v", filePath, err)
 	}
-
-	sinkMetadata := g_sinkMetadata
-	tmpMap := make(map[string]*uiSink)
-	for k, v := range sinkMetadata {
-		tmpMap[k] = v
+	if pluginName != baseProperty && pluginName != baseOption {
+		if nil == metadata.About {
+			return fmt.Errorf("not found about of %s", finame)
+		} else {
+			_, metadata.About.Installed = m.registry.Get(SINK, pluginName)
+		}
 	}
-	fileName := path.Base(filePath)
-	common.Log.Infof("Loading metadata file for sink: %s", fileName)
-	tmpMap[fileName], err = newUiSink(ptrMetadata)
+	g_sinkMetadata[finame], err = newUiSink(metadata)
 	if nil != err {
 		return err
 	}
-	g_sinkMetadata = tmpMap
+	common.Log.Infof("Loading metadata file for sink: %s", finame)
 	return nil
 }
+
 func (this *uiSinks) setCustomProperty(pluginName string) error {
 	fileName := pluginName + `.json`
 	sinkMetadata := g_sinkMetadata
@@ -367,7 +363,18 @@ func GetSinks() (sinks []*pluginfo) {
 		node := new(pluginfo)
 		node.Name = strings.TrimSuffix(fileName, `.json`)
 		node.About = v.About
-		sinks = append(sinks, node)
+		i := 0
+		for ; i < len(sinks); i++ {
+			if node.Name <= sinks[i].Name {
+				sinks = append(sinks, node)
+				copy(sinks[i+1:], sinks[i:])
+				sinks[i] = node
+				break
+			}
+		}
+		if len(sinks) == i {
+			sinks = append(sinks, node)
+		}
 	}
 	return sinks
 }

+ 21 - 5
plugins/sourceMeta.go

@@ -48,7 +48,7 @@ func newUiSource(fi *fileSource) (*uiSource, error) {
 
 var g_sourceProperty map[string]*sourceProperty
 
-func readSourceMetaFile(filePath string) error {
+func (m *Manager) readSourceMetaFile(filePath string) error {
 	fileName := path.Base(filePath)
 	ptrMeta := new(fileSource)
 	err := common.ReadJsonUnmarshal(filePath, ptrMeta)
@@ -58,6 +58,11 @@ func readSourceMetaFile(filePath string) error {
 	if 0 == len(ptrMeta.ConfKeys["default"]) {
 		return fmt.Errorf("not found default confKey %s", filePath)
 	}
+	if nil == ptrMeta.About {
+		return fmt.Errorf("not found about of %s", filePath)
+	} else {
+		_, ptrMeta.About.Installed = m.registry.Get(SOURCE, strings.TrimSuffix(fileName, `.json`))
+	}
 
 	yamlData := make(map[string]map[string]interface{})
 	filePath = strings.TrimSuffix(filePath, `.json`) + `.yaml`
@@ -80,7 +85,7 @@ func readSourceMetaFile(filePath string) error {
 	return err
 }
 
-func readSourceMetaDir() error {
+func (m *Manager) readSourceMetaDir() error {
 	g_sourceProperty = make(map[string]*sourceProperty)
 	confDir, err := common.GetConfLoc()
 	if nil != err {
@@ -93,7 +98,7 @@ func readSourceMetaDir() error {
 		return err
 	}
 
-	if err = readSourceMetaFile(path.Join(confDir, "mqtt_source.json")); nil != err {
+	if err = m.readSourceMetaFile(path.Join(confDir, "mqtt_source.json")); nil != err {
 		return err
 	}
 
@@ -101,7 +106,7 @@ func readSourceMetaDir() error {
 		fileName := info.Name()
 		if strings.HasSuffix(fileName, ".json") {
 			filePath := path.Join(dir, fileName)
-			if err = readSourceMetaFile(filePath); nil != err {
+			if err = m.readSourceMetaFile(filePath); nil != err {
 				return err
 			}
 			common.Log.Infof("sourceMeta file : %s", fileName)
@@ -129,7 +134,18 @@ func GetSources() (sources []*pluginfo) {
 			continue
 		}
 		node.About = v.meta.About
-		sources = append(sources, node)
+		i := 0
+		for ; i < len(sources); i++ {
+			if node.Name <= sources[i].Name {
+				sources = append(sources, node)
+				copy(sources[i+1:], sources[i:])
+				sources[i] = node
+				break
+			}
+		}
+		if len(sources) == i {
+			sources = append(sources, node)
+		}
 	}
 	return sources
 }