Node-Red是一个可视化的编程工具。它允许程序员通过组合各部件来编写应用程序。这些部件可以是硬件设备、Web API(如:WebSocket in和WebSocket out)、功能函数(如:range)或者在线服务(如:email)。

Node-RED是一种全新的编程工具,以有趣的方式将硬件设备,API和在线服务连接在一起。它提供了一个基于浏览器的编辑器,可以很容易地使用调色板中丰富的节点连接成流程,只需单击一下即可完成部署。Node-RED构建在Node.js之上,充分利用其事件驱动的非阻塞模型。 这使得它非常适合运行在低成本的硬件(如Raspberry Pi以及云)上。

Node-Red 有三种安装方式:

  1. 本地运行
    • 直接安装在Windows,Linux 或者Mac,Docker
  2. 在设备上
    • Raspberry Pi
    • BeagleBone Black
    • Interacting with Arduino
    • Android
  3. 在云上
    • IBM Bluemix
    • SenseTecnic FRED
    • Amazon Web Services
    • Microsoft Azure

本文基于Windows 10下进行安装说明,Windows10下进入cmd模式,所有安装过程都是在cmd模式下进行的。

安装Node.js

从Node.js官网下载最新的8.x LTS版本。官网自动提供最适合你的系统的版本。

运行下载的msi文件。安装Node.js需要本地管理员权限;如果你不是本地管理员,安装过程中需要输入管理员密码。安装过程中,接受默认值。安装完成后,关闭命令窗口并打开新的命令窗口确保新的环境变量生效。

在新的窗口输入下面的命令检查Node.js和npm正确安装。

使用Powershell:

node --version; npm --version

使用cmd:

node --version && npm--version

输出结果如下:

v8.9.0
5.5.1

安装Node-RED

将Node-RED安装为全局模块,将命令node-red添加到您的系统路径中。 在命令提示符下执行以下命令:

npm install -g --unsafe-perm node-red

安装完成,就可以运行Node-RED。

运行

安装完成后,运行Node-RED的简单方法是在命令提示符下使用node-red命令。如果已经将Node-RED安装为全局npm软件包,则可以使用node-red命令:

C:>node-red

这会将Node-RED日志输出到终端,您必须保持终端打开,以保持Node-RED运行。

Node-RED的结构框架

Node-Red由两部分组成。一部分是用户可见的数据流的编辑界面,另一部分是数据流的执行。刚刚在cmd中打开node-red时,我们已经见过了数据流的执行提示;打开浏览器访问http://localhost:1880,就可以看到Node-Red的编辑界面。

Node-Red编辑界面

数据流的编辑界面由四部分组成。最左边是已定义的各种node的列表,我们称之为控件区;中间是一个工作区,用户可以拖放node到工作区来创建node的实例,Node-Red为每个node实例赋予了唯一的ID,通过双击node实例来编辑单个实例,通过连接node的in和out创建数据流,node实例会记录out口连线的信息,每条线会记录目标node实例的信息;最右边是debug node的输出区及node的帮助信息显示区。右上角有‘Deploy'(或者"部署")按钮,用来把编写的程序保存到本地并执行。

数据流的执行:通过读取用户编辑的数据流信息,可以知道node的类型及可编辑部分的值,据此来创建node的可执行实例;通过读取编辑时连线的信息,可以得到可执行实例间的数据关系,实例间的数据发送和接受是利用Node.js的event模块实现的。

注意:在Node-Red的根目录下,可以通过执行‘node red.js'运行Node-RED。Node-Red编辑完成的数据流默认保存在flows_.json,可以通过执行‘node red.js flows_.json',在不启动浏览器的情况下执行已经编辑完成的程序,这个在实际部署的时候非常有用。

注意,关闭浏览器并不会导致已经部署好的数据流停止执行。在windows系统中,需要停止node-red中的批处理命令,可以输入ctrl+c。在树莓派中,可以命令行中输入node-red-stop。

hello world

所有与编程相关的教程总是喜欢从hello world开始,node-red也不例外。作为可视化的编程工具,nod-red的hello world与其它编程语言有些不一样。

拖拽输入与输出节点

启动node-red以后,在浏览器中,将控件区内的输入节点"inject"与输出节点"debug",使用鼠标左键拖入工作区内。

拖拽输入

拖入以后发现"inject"变成了"时间戳","debug"变成了"msg.payload",这是正常现象。如果你的计算机或是树莓派运行在英文环境下,理所当然,工作区内的节点的名字会变成英文。原因在于,节点位于控件区的时候,表明它是"某种"节点,这种节点叫做"inject"或是"debug"。拖到工作区以后,它就是"某个"节点,具体到某个节点,当然就是有名字的。前者是抽象的,后者是具体的。为了方便表述,前者可以称之为控件,后者可以称之为节点。如果有面向对象的编程经验,可以很轻松的理解,"inject"与 "时间戳"的关系,其实很像类与对象的关系。

修改节点的配置

双击"时间戳",在屏幕的右侧会弹出如下窗口:

修改节点

点击"内容"选项后边的小三角,在下拉菜单选择文字列,并在输入框内输入"hello world",然后点击完成。

编辑节点

可以观察到,工作区中的"时间戳"变成了"hello world"。

效果展示

连接输入与输出节点

在node-red中用"flow"来表示数据的流向,中文意思是"数据流"。这其实是一个很生动的翻译,这个"流"与小溪流的"流"是同一个含义,只不过前者流淌的是数据,而后者流淌的是水。数据要从输入节点到达输出节点,如何连接呢?

很简单,用一条线连接。

Node-red总是默认数据从左流向右,所以输入节点都有一个特点:数据接口在右侧,见下图的标记;输出节点也有一个特点,数据的接口在左侧,见下图:

输入节点

还有一些节点是特殊的,既有输入又有输出,那么左右两侧都有数据的接口,见下图:

输入节点

注意,这里的输入与输出都是相对于"整个数据流"来说的。其实单单对于输入节点来说,它负责向外输出一个数据,比如"hello world"。那么为什么把它叫做输入节点呢?因为对于整个数据流来说,输入节点为数据流输入了一个"hello world"信息,所以称之为输入节点。

使用鼠标按住左键,从上图接口1连接到接口2,即可完成输入输出的节点连接。用一条线连接输入输出节点,大概是最简单最直观的构建数据流的方式了。

输出节点

部署

我们发现节点的右上角有一个蓝色的圆点,这个圆点的意思是,此节点还没有部署和保存。部署按钮位于工具栏,在浏览器的右上角。虽然名字叫做部署,但其实有部署和保存两种功能。保存的含义相信大家都能理解,部署对于没接触过的人可能就比较陌生了。部署通俗来说就是"让它们工作",我们在工作区放置了一些节点,通过部署按钮,可以让它们工作起来。

其下拉菜单中还有一些别的选项如下。

开始部署

点击部署按钮,如果数据流与节点都没有问题,会有"部署成功"的弹窗提示。节点上蓝色的圆点也消失了。

部署成功

调试

"inject"节点可以手动输入消息,节点左侧有一个小按钮,点击按钮可以手动注入消息,见按钮1。在点击inject节点的按钮之前,必须确保debug节点是可用的,即按钮必须是"伸出来"的,如按钮2,而不是像按钮3一样"缩回去",按钮"缩回去"的debug节点不工作。点击按钮可以切换节点是否工作。

按钮调试

点击按钮1,屏幕上方会提示:"成功注入:hello world"。 在屏幕右侧有调试窗口,窗口内可以看到一条消息,且内容正好是输入节点的信息:"hello world"

结果展示

inject介绍

Node-red控件很多,我们可以根据自己的需要来选择。每个控件官方都带有详细的说明,接下来介绍一个典型的输入控件——inject。

inject的说明信息

前边hello world的例子中,我们已经认识了输入节点,接下来详细介绍它。Node-red内部自带了对控件的介绍。鼠标悬停在某个控件上,就会出现一些提示信息,例如:

提示信息

点击节点时,页面右侧也会出现提示信息:

右侧提示信息

阅读这些信息,就能对节点的使用有大概的了解。不过由于信息都是英文的,为了方便读者,我们会把常用控件的英文提示翻译过来。

以下是Inject的节点帮助信息

手动或定期地将消息注入到流中,消息有效负载可以是多种类型,包括字符串、JavaScript对象或当前时间。

输出

  1. 多种多样的载荷
    消息需要配置载荷
  2. string类型的topic
    节点主题的配置是可选的。(可以配置topic,也可以不配置)
    详细说明
    注入节点可以启动带有特定载荷的流。默认的载荷是时间戳,时间戳的意思是从1970年1月1日到当前时间,经历了多少毫秒。
    该节点还支持注入字符串、数字、布尔值、JavaScript对象或流/全局上下文值。
    默认情况下,通过单击编辑器中的按钮来手动触发节点。它也可以被设置为定期注入或根据时间表(在特定的时间段内)进行注入。
    它还可以配置为在每次启动流时注入一次。
    可以指定的最大时间间隔是596小时/24天。但是,如果您查看的时间间隔超过一天,您应该考虑使用一个调度器节点来处理断电和重新启动。
    注意: "时间间隔"和"在特定时间"选项使用标准CRON系统。这意味着如果填写的是20分钟,那么这20分钟会在整点20分以后,例如1:20,1:40,而不是从程序开始运行的20分钟,例如程序是2:11开始运行的,那么不使用CRON系统的话,下个20分钟就是2:31。如果你想从现在开始每20分钟-使用"间隔"选项。
    注意: 要在一个字符串中包含一个换行符,您必须使用一个函数节点来创建有效负载。(意思就是注入节点的字符串没办法包含换行符)
    帮助信息已经说得很清楚了,不过由于输入节点非常常用,且其它的输入节点跟它的原理都一样,所以我还是啰嗦一下,举常用的例子来说明。

输入时间戳

添加输入节点(inject)与调试节点(debug)。连线,并且部署。

添加时间戳

点击时间戳的按钮,然后在调试窗口下应该能看到这样的一串数据。

时间信息

这个就是时间戳的值。关于时间戳的解释,节点帮助信息已经说得很清楚了,时间戳的意思是从1970年1月1日到当前时间,经历了多少毫秒。也就是从1970年1月1日到写教程的时候,经历了1579250830938毫秒。

时间信息转换

可以通过一个在线工具查看如此一来就能方便的把毫秒转换为当前时间了。

其实debug控件也很人性化的提供了转换功能,在调试窗口点击数字就能看到当前时间了。

再点击一下,能转换为十六进制的数据。虽然这个功能并没有什么用。

输入数字与数组

Inject节点的内容可以配置为数字或数组。

数据转换

数字建议输入十进制,可以是整数也可以是小数。不建议输入十六进制,如果确实要输入十六进制的数可以转换为十进制输入。其实这是同一个数,0xc=12。

输入小数

进制转换

如需输入数组,在"内容"一栏,要选择二进制,并且手动添加英文字符中的括号与逗号。注意,是英文字符的","。编程语言中几乎不会出现任何中文符号,后边见到的所有的与程序相关的符号都是英文。

输入数组

使用debug节点观察到的输出的效果如下:

输出效果

输入ASCII码

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统。

在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)、以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示。

由此可以看出,ASCII码对于英文字符的显示很重要。例如,我们想让电脑显示大写字符H,需要用H对应的ASCII码,也就是72来表示。这个72并不代表数字的72,因为数字的72由数字7与数字2的ASCII码来表示,也就是55与50。

Inject的二进制流可以很方便的进行ASCII码与字符的转换。在"内容"输入框中输入一个单词,比如Hello。然后点击输入框右边的扩展按钮。可以看到一个新的页面,"缓冲区编辑器"。点击此页面的"完成"按钮。

缓冲区编辑器

可以发现内容输入框已经自动转换为一个数组,而数组的内容正好是"Hello"的每个字符相对应的ASCII码。

数组转换

在debug节点中可以看到这个数组:

节点数组

点击数组,可以展开并显示为十六进制。

十六进制显示

点击可以切换为字符,正是我们输入的"Hello":

字符转换

从这个例子来看,如果不是因为可以输出数组,那么把字符转为ASCII码,与直接输出文字列的功能是否雷同?不会的。有一些控制符,比如回车、换行,无法在文字列中输出。但是它们有对应的ASCII码,通过查表可以得知:回车换行对应的ASCII码值分别是13与10,在"内容"输入框中手动输入13与10即可。

主题与名称

节点属性中也可以配置主题(topic)与名称(name)。

我们拖进来一个inject节点,并且更改内容为文字列,Hello World。

配置主题

可以工作区中,节点的名字也是Hello World。

节点名称

双击debug节点,观察输出,默认是msg.payload。通过下拉菜单把它改为完整信息,并部署。

节点名称

点击inject节点的输入按钮,可以再调试窗口看到以下信息:

节点名称

_msgid毫无疑问就是节点的ID,既然是ID当然也就是独一无二的。

Topic就是主题,里边的内容为空,是因为我们没有设置。

Payload意思是有效负载或载荷,就是inject节点的内容输入框里的东西。这个名词让人感觉怪怪的,但其实很好理解。如果把数据包比喻为一辆货车,那么ID就是车牌号,topic可以是货车的品牌,payload就是货车上的货物。

通过以上的观察,我们可以发现,一个数据包至少包括了ID,topic与payload三个成员。

接下来编辑inject节点,在"主题"输入框内输入topic:

编辑节点

重新部署,并点击inject节点的按钮,对比调试窗口的信息,可以发现, 新的数据包增加了topic的内容。

信息对比

接下来修改name。在"名称"输入框内填写name。

修改名称

可以发现工作区中节点的名字发生变化。

框图对比

部署并点击输入的按钮,可以观察到,name的信息并没有出现在debug窗口。

以上实验说明:name属性只影响了工作区中节点的外观,并没有影响流中数据包的内容。

Topic是数据包本身带的一个属性,其信息会随着数据包下发。在后续的学习中,有时会用到topic来判断数据包的来源。

其它控件的topic与name属性与此类似,明白inject控件的topic与name如何使用以后,你就可以举一反三了。

重复发送

到目前为止,我们使用inject节点给流程注入数据包的方法都是点击节点上的小按钮,这一点也不酷,程序根据我们的设定自己就能工作起来才好玩。而inject节点就有这么一个类似于"闹钟"的功能。

双击编辑inject节点,通过下拉菜单选择"重复"输入框后的内容为"间隔",然后下边一栏会紧接着出现每隔1秒。数字与单位都可以设置。比如设置为每隔5秒,然后点击完成,部署。

重复发送

会发现调试窗口以5秒为间隔收到了很多消息。

注意,此时即便是关掉了浏览器,消息依旧会发送。在powershell运行了node-red的服务器,只要服务器没有关闭,流程当然会正常运行。

如果需要让这个流停止工作,可以采用以下办法:

  1. 关掉服务器。可以选择关掉powershell,或者在powershell里按下ctrl+c,终止批处理操作。不过这种做法会使其它的流也停止工作。
  2. 修改流,断开节点之间的连线并部署。
  3. 关掉流,双击流的标签,把状态切换成"无效"。

debug控件介绍

debug的说明信息

Debug控件最主要的作用,是打印出一些信息,方便程序调试。前边已经多次使用了debug控件了,相信大家对他已经不陌生了。接下来进行更加详细的介绍。

在debug侧栏选项卡中显示选定的消息属性,可以选择运行时日志。默认情况下,它显示消息的有效负载。

详细说明

debug侧栏提供了被发送的消息的结构化视图,使其更容易理解它们的结构。

JavaScript对象和数组(arrays)可以根据需要折叠和展开。缓冲区(buffer)对象可以作为原始数据显示,也可以作为字符串显示。

除了每条消息之外,调试侧栏还包含关于接收消息的时间、发送信息的节点和消息类型的信息。点击源节点id将会在工作区中显示该节点。

节点上的按钮可以用来启用或禁用它的输出。建议禁用或删除未使用的调试节点。

节点还可以配置为将所有消息发送到运行时日志,或者将短(32个字符)发送到调试节点下的status(状态)文本。

前边已经使用过了debug控件的以下功能,如果忘记了就自己往前翻一翻书本。

  1. 点击歇息切换显示,可显示ASCII码、十进制、十六进制。
  2. 展开buffer或数组。
  3. 选择输出的内容为payload还是完整信息。
  4. 观察到数据包的时间。

向系统控制台发送消息

配置节点属性如下。使用一个inject节点发送消息。

inject节点发送消息

可以在系统的控制台,比如powershell,也就是输入"node-red"打开node-red服务器的地方,可以看到以下内容:

控制台显示

debug控件在调试中的应用

Debug节点具备快速定位的功能。新建两条流,每条流都有inject节点与debug节点,inject节点的内容分别为flow 1与flow 2,其它都默认,然后部署。

部署

随意点击两个Inject节点的按钮,观察调试窗口。点击node,可以发现系统自动帮我们定位到了接收这个消息的debug节点。

定位接收消息

闪烁提示

然后修改debug节点的名称,分别改为d1和d2。部署,并随意点击两个inject节点的按钮。

修改节点名称

此时可以发现,消息的来源已经被注明为d1或是d2。

消息来源

放置debug节点并修改它的name,这是一个好习惯。它可以帮助你在一堆数据包中,快速找到你需要的数据。

如果信息实在多的话,调试窗口自带有过滤功能,可以过滤掉不需要的消息。操作如下。

过滤节点

完成以后,则调试窗口只剩下刚刚被勾选的d1,D2的消息就不见了。

过滤消息

也可以点击debug节点右侧的小按钮,按钮就自动缩回去了,表示这个节点不再输出消息。

缩回操作

灵活地掌握debug节点的定位与数据的筛选,可以极大提升调试的效率。比如某个节点应当输出某个消息,但是不知道输出的消息到底对不对,放置一个debug节点就可以观察到这个消息了。

file控件介绍

File节点分为两种,功能分别是file in与file out。

File节点

file的说明信息

File in 控件

读取文件的内容,输出为字符串或二进制缓冲区。

输入

字符串形式的文件名

如果不在节点配置中设置,则该属性设置为需要读取的文件名。

输出

字符串或数组的输出

文件的内容是一个字符串或二进制缓冲区

文件名

建议配置文件名的选项,可以设置为路径\文件名

错误对象

弃用:如果在节点中启用,当节点遇到读取文件的错误时,它将发送一条没有有效负载的消息,并且这个错误属性设置为错误细节。这种行为模式被弃用,默认情况下新的节点也不会启用这种模式。

详细信息

文件名应该是绝对路径,否则应当是node-red进程工作目录的相对路径。

在Windows上,路径分隔符可能需要转义,例如:\Users\myUser.

可选地,一个文本文件可以被分割成几行,每一行输出一条消息,或者将二进制文件分割成更小的缓冲块——块大小是依赖于操作系统的,但通常是64 k(linux/mac)或41 k(Windows)。

当分割成多个消息时,每条消息都有一个"部分的"属性需要设置,这个"部分"应当形成一个完整的消息序列。

应该使用Catch节点捕获和处理错误。

File out 控件

写消息的载荷到一个文件,或者把内容添加到文件末端,或者替换现有的内容。或者,删除该文件。

输入

字符串形式的文件名

建议配置文件名的选项,可以设置为路径\文件名

详细信息

每条消息有效负载都将被添加到文件的末尾,可以选择在每条消息之间附加一个换行符(\n)字符。

如果msg.filename被使用,每次写入后,文件都将被关闭。为了获得最佳性能,使用一个固定的文件名。

它可以被配置为覆盖整个文件而不是追加。例如,当把二进制数据写入文件时,比如图像,应该使用这个选项,并且应该禁用追加换行的选项。

或者,可以配置这个节点来删除文件。

file的使用

拖拽一个inject节点,用于输入时间戳,再拖拽一个file节点,进行如下编辑:

file节点使用

1 文件名。编辑页面下边有提示,告诉我们应该保存一个绝对路径。如何知道某个文件夹的绝对路径呢?你可以先在电脑的文件管理器中找到这个文件夹,也就是在"我的电脑"里打开这个文件夹,接下来把鼠标挪到地址栏,点击

磁盘资料

然后按下ctrl + c:

复制文本

路径就复制下来了。如果你想在这个文件夹下新建一个文件,可以在编辑file节点的页面,文件名后边的输入框,也就是数字1标记的地方,粘贴,然后在路径后手动输入"\你需要的文件名",就比如上图的"E:\node-red数据\保存时间戳.txt",然后把数字3标记的小方框勾选上去。

如果没有写路径的话,文件会被保存在"C:\Users\Administrator"下。

数字2标记的"行为"也有一些选项,由于含义明确,不再啰嗦了。

追加文件

name之前也讲过了,它只影响节点中浏览器的外观。

名字外观

然后部署,点击inject按钮发送一些时间戳,可以看到文本文档里有一些信息,说明file节点已经可以工作了。

文档信息

file in 的使用

File in控件的左右都有灰色的小方块,而inject只是右侧有一个小方块,他们两者的功能类似,都是输入控件。通过对比可以猜出来,file in 控件左边的小方块,与inject左边的小按钮一样,用于输入触发。

接下来用inject,file in与debug构建这样的一条流

输入输出流

点击inject的按钮,在调试窗口可以看到以下信息:

调试信息

你可以把file in 节点的输出选项变一下,每个选项到底是什么作用,自己来体会下吧。

字符选择

另外,讲述file控件的时候我们都用了文本文档来举例,我生怕读者会认为file控件只能用到文本上,那就大错特错了了。实际上,据我所知,有人用node-red做图像识别,那么node-red肯定是能够读取图像的。

switch控件介绍

Switch本意是开关、转换,用在node-red里有点像一个岔路口,node-red里边有数据流,数据流可能有不同的分支,在"岔路口"管理数据流向的,就可以是switch控件。控件根据数据的一些属性来判断它的流向。就比如你到了公共卫生间,你是男生,就去左边的男卫生间;女生去右边的女卫生间。Switch控件可以根据数据的topic或是payload,或者其它属性来判断数据该发给哪一个出口。

Switch控件属于"function"类型的控件,function一般翻译为功能或者函数。

Switch控件

switch的说明信息

在附带这些说明的时候,我其实是忐忑不安的。因为我发现,这个说明信息可能把简单的问题复杂化了。我在没有看这些说明的时候就摸索出了这个节点的基本用法,看了说明以后反而有点不知所云。这是因为,switch节点的高级用法我们可能暂时没有用到,因此如果读一下信息让你感到疑惑,你大可以跳过这段,毕竟我们以应用为主。

以后别的控件的说明信息也可以先跳过,掌握用法以后再来看,可能会好一些。

根据其属性值或序列位置路由消息。(路由在这里可以理解为数据转发)

细节

当消息到达时,节点将评估每一个已定义的规则,并将消息转发到任何匹配规则的相应输出。

可以选择配置为,一旦找到匹配的规则,节点就可以停止评估规则。

可以根据单个消息属性、流或全局上下文属性或JSONata表达式的结果来评估规则。

规则

有四种类型的规则:

数值:规则是根据配置的属性进行评估的。

序列:规则可用于消息序列,如分割节点生成的序列规则。

表达式:可以提供一个JSONata表达式,它将对整个消息进行评估,并且如果表达式返回true值,则会匹配。

其它规则:如果没有匹配的规则,则可以使用另一种规则来匹配。

处理消息序列

默认情况下,节点不会修改消息的msg.parts属性,这是序列的一部分。

重新创建的消息序列选项可以为匹配的每条规则生成新的消息序列。在这种模式下,节点将在发送新序列之前缓冲整个传入序列。运行时设置nodeMessageBufferMaxLength 可以用来限制有多少消息节点将被缓冲。

switch判断数值

在程序设计中,最重要的不是写程序,而是设计。就像建筑、机械等行业的要画设计图、施工图,程序设计的思路也有必要用图的形式画出来。画图的过程就是思考的过程,由于其直观性,画图的过程本身又促进了思考。在软件工程中,已经发展出了很多种实用的图,为软件产品设计的质量提供保证。接下来我们就来学习程序设计中最简单的一种图——"程序流程图"。

在程序设计之前,我们先来梳理一下任务:可能会有一些数据需要我们判断大小,然后把数据发到不同的输出分支。有点像你提着三个篮子去摘苹果,大苹果放在篮子1,中苹果放在篮子2,小苹果放在篮子3。我们就以"10"作为判断的标准,把1,10,100作为测试数据。分析任务,可以得到以下流程图。

数据流程图

测试数据的输入使用inject节点,三个输出使用debug节点,相信大家早已轻车熟路。然后拖入一个switch节点。按图示修改:

节点拖入

编辑switch节点

改完以后发现switch节点的输出变为3个了,从上到下分别是输出1,输出2与输出3。连接并部署。

部署switch节点

分别点击3个inject节点的输入按钮。可以在调试窗口观察到以下现象。可以看出,100是OUT1节点收到的,也就是第一个判断条件(>10)的输出,100>10,没有问题。其它的数也根据判断条件正确输出,说明我们的任务完成了。

调试窗口

switch判断数组

刚刚的例子只能判断单个数字,实际使用中,我们常常需要判断数组的某一位。接下来,在刚刚的例子上进行一点点的修改,来实现可以根据数组的第二位的情况来分发数据。

注入内容改为数组。

编辑inject节点

Switch节点需要注意属性,是msg.payload[1]!因为数组下标从0开始,所以数组的第一位是msg.payload[0],第二位才是msg.payload[1]。

除了刚刚使用的大于,等于和小于,还有很多判断条件,感兴趣的可以尝试下。我这里随便用了几个。

顺带提一下,在switch的说明信息里,有很多关于sequence rules和JSONata exp的描述,这些可能是很有用的判断条件,不过我们暂时没有用到,因此看不懂说明信息。感兴趣的可以查一下。

编辑switch节点

switch判断topic

前边两个例子都是根据msg.payload属性来判断的,msg里边的其它属性也可以作为判断条件。

拖入3个inject节点,topic要填写,我填写的topic是"apple","banana"和"orange"。Switch节点进行如下修改。

节点修改

连线并部署。点击inject按钮观察现象。

部署现象

我想,除了点击一下apple的输入按钮,OUT1与OUT3都收到数据以外,其它的现象都和你预料的一致。Apple为什么会输出两次?

这是因为条件3是包含"e",apple也是包含"e"的,所以apple是满足条件3的。

如果想让apple满足条件1以后就不再判断条件3,可以选择"接受第一条匹配消息后停止"。

函数控件介绍

函数控件在node-red中是重点,也是难点。由于其功能强大,能做的事情很多,所以它重要;事实上,函数控件中的"函数"一词,翻译为中文"功能"也是可以的。但是,函数是需要直接编写代码的,所以说也是难点。

节点帮助

一个JavaScript函数块,用来处理节点接收到的消息。 这些消息作为一个名为msg的JavaScript对象传入。

按照惯例,它会有一个msg.payload属性,包含消息正文。 函数被期望返回一个message对象(或多个消息对象),但是可以选择不返回任何东西以阻止流。

详细信息

有关编写函数的更多信息,请参见在线文档

发送消息

这个函数既可以使用"return"向流中的下一个节点传递消息,也可以调用node.send(messages)。

它可以返回或是发送

一个单一的消息对象——传递给连接到第一个输出的节点 一组消息对象——传递给连接到相应输出的节点

如果数组的任何元素本身都是一组消息,那么多条消息就会被发送到相应的输出。

如果返回null,无论是单独还是作为数组的元素,都不会传递任何消息。

日志和错误处理

要记录任何信息或报告错误,可以使用以下功能:

node.log("Log message")

node.warn("Warning")

node.error("Error")

Catch节点也可以用来处理错误,为了调用Catch节点,将msg作为第二个参数传递给node.error。

使用函数控件实现多个输出

前边使用switch控件时我们已经实现了根据不同的条件,来实现多个输出。Switch控件是一种功能控件,而函数控件也是功能控件。甚至可以说,函数控件可以通过编程实现所有功能控件的功能。接下来,我们尝试用函数控件实现多个输出。

最简单的函数控件用法

拖入一个inject、function与debug,无需任何修改,直接用线连接三个节点。部署,并点击inject的输入按钮,观察调试窗口。可以看到debug节点打印的调试信息。你可能会说,什么嘛,不加这个函数节点,直接连接inject与debug节点,不也是这个效果嘛!这个函数节点有什么用?

它的作用就是,把消息原封不动输出。原封不动的输出,也是一种功能,最简单的功能。我们双击函数节点,来看一下里边的内容。

编辑function节点

只有一行代码,return msg,返回消息。

回过头来,看函数控件的说明信息。用来处理节点接收到的消息,消息作为一个名为msg的对象传入。可以使用"return"向下一个节点传递消息。也就是说,函数控件的输入时msg对象,输出也是msg对象。在输出之前,可以对msg对象进行处理。所谓对象,意思是这是一个msg实例,是具体的数据,不是抽象的,更不是msg跟异性朋友处对象。

既然可以原封不动返回msg,当然也可以不返回。只要把语句改为return null即可。Null的意思是空值,也可以说没有值。修改代码为:

编辑function节点

重新部署,无论现在如何点击inject节点的输入按钮,debug节点都不会打印出任何消息了。

在函数节点中创建并返回多个消息

函数控件中,除了可以处理输入的msg对象以外,也可以自己定义一些msg对象。另外,说明文件中,有介绍说function可以返回一个对象,或是用数组传递一组对象。我们先尝试自行建立一组对象并传递。

修改函数节点:取个合适的名称,添加如下代码,并把输出改为两路。

var msg1 = { payload:"first out of output 1" };
var msg2 = { payload:"second out of output 1" };
var msg3 = { payload:"third out of output 1" };
var msg4 = { payload:"only message from output 2" };
return [ [ msg1, msg2, msg3 ], msg4 ];

添加代码

拖入两个debug节点,命名,并分别接到函数节点的两路输出去。然后点击inject的按钮,观察调试窗口的现象。

拖入debug节点

调试窗口打印

我们发现,OUT1节点一口气收到了3条消息,OUT2节点收到了一条消息,且消息的内容我们刚刚输入的代码有关系。跟输入的消息没有任何关系,因为输入的是时间戳。 关注最后一句并对比分析:

return msg;//一个返回值
return [ [ msg1, msg2, msg3 ], msg4 ];//一组返回值

可以得出结论,如果想得到多个返回值,需要把返回值放到英文的中括号里。在8.4.3小节里,恰好在inject的内容里,输入数组时,也是放在中括号里。这说明,多个返回值需要放到一个数组中,数组用中括号括起来,用逗号间隔开。而数组元素的顺序,也就是输出对象的顺序,比如刚刚的OUT1收到的是msg1,msg2与msg3,而OUT2收到的msg4。

另外,虽然名为数组(array),但是其边的元素并不是数字,而是msg对象,所以,更贴切的叫法应该是:对象组。

通过判断语句进行数据分类

Switch控件可以进行数据的分流,它属于功能控件。函数控件是功能控件里,功能最强的,它可以实现switch控件的功能。

前面讲解switch控件时,使用过判断数字的例子,这里用函数控件来实现它。

输入与输出不变,把函数节点替换掉switch节点,并修改代码如下:

修改代码

分别点击3个inject节点的输入按钮。可以再调试窗口观察到以下现象。

点击输入按钮

可以看出,所有数据都根据判断条件正确输出,说明我们的任务完成了。

然后看一看通过主题来判断的例子。跟判断数字没有多大的不同,只需要把被判断的条件改为msg.topic就可以。

使用函数控件处理数据

上一小节使用函数控件进行判断与多个输出的情况下,函数控件可能还没有switch好用。但是在这一节,函数控件的优势就能完全体现出来。接下来使用函数控件可以进行数据的处理。

单个数字的计算

接下来尝试用函数节点对数字进行计算。拖入inject节点,并把输入的内容改为数字;拖入debug节点用来观察现象,拖入函数节点并做如下修改: 其中,var newMsg = {payload: msg.payload * 2 }语句的意思是,新建一个对象,名为newMsg,它有payload属性,并且payload的值为msg.payload的两倍。最后返回的是newMsg,也就是新建的这个对象。

新建对象

部署并运行,可以看到现象是,调试窗口显示的数值是输入的两倍。说明函数节点的功能实现了。

我们知道,消息是通过msg对象传过来的,可不可以不新建一个对象,直接修改msg.payload呢?当然是可以的。将函数节点内的代码修改为

msg.payload = msg.payload*2;     
return msg;

部署,可以发现结果一样正确。由于这个代码实际的处理顺序是从右向左,也就是msg.payload先乘以2,再赋值给msg.payload。出于方便理解的角度,我们总是用一个变量来暂时储存一下msg.payload计算过的值。代码修改为:

var temp = msg.payload*2; 
msg.payload = temp;      
return msg; 

使用数组进行数据的截取与组装

通常,在实际通信的时候不会只传递一个数组,信息的传递依赖于数组,数组中不同位置的数字也会有不同的含义,例如,第一个数字表示ID,第二个数字表示数组长度,第三个数字表示温度等等,一般会有一个通信协议来规定。所以,如何取出有用的数据,或者按要求把数据进行组装很重要。 现在,假如收到了一串数据,数据共16位,我们只用到后8位,前边的8位都不要了,如何来操作?

数据解析

通过分析可以得知:

ARR2[0] = ARR1[8];
ARR2[1] = ARR1[9];
......
ARR2[7] = ARR1[15];

但是写代码的时候一般不会这么复制粘贴。这是循环操作,用for语句可以增加效率。

var temp = new Buffer([0,0,0,0,0,0,0,0]);   
for(var i = 0;i <8 ;i++)                  
temp[i] = msg.payload[i+8];             
msg.payload = temp;                   
return msg;

我们新建一个temp数组,有8个元素并初始化为0; 通过for循环,把msg.payload[i+8]的值赋给temp[i],i可以是0到7。

然后板temp作为msg的payload返回。

接下来添加inject与debug节点搭一个测试环境。Inject节点内容为数组。

搭建测试环境

数据流

编写程序

部署并观察调试窗口如下:

调试窗口打印

说明经过函数节点以后,我们成功截取了数组的后8位。

模拟温度数值的计算

由于通信方式的限制,数据在发送时,常常要转换为8位的字符来发送。在数字电路里,只有高电平和低电平两种情况,所以数据在发送时,就只能使用1和0个数字。这个1或0,称之为1位。通常,8位可以传输一个字符。2的8次方是256,所以小于256的数字,可以用8位来传输。而大于或等于256的数字,就要分成2个8位传输了。另外,8位的数据可以用两个十六进制的数来表示,比如0101 1010可以表示为0x5a,所以通常会用十六进制来表示通信协议中的数。

例如,我们现在想传输十进制的数字14859,大于256,所以要分包传送。十进制转化为二进制是0011 1010 0000 1011,在通过硬件发送数据时,如果不考虑校验或者起始位以及高位还是地位在前,就只考虑数字的发送,那么总线上的电平可能是低低高高,高低高低,低低低低,高低高高。把这个数字写成十六进制,是0x3a与0x0b,或0x3a0b,0x用来表示这是个十六进制的数字。0x3049可以通过这种方式换算成10进制:数字拆成单个字符来分析,a到f的数对应10到15,在第几位上,就乘以几个16。0x3a0b = 3×16×16×16+10×16×16+11=14859。或者直接使用16进制进行计算,0x3a0b=0x3a×256+0x0b。

当然,通信协议是可以进行特殊的规定,例如表示4位的温度,34.56°C,要通过十六进制的数字进行分包发送,就是0Xd80/100。我们现在规定,十六进制的温度高位×256+低位=温度×100。接下来尝试解析温度的数据。

我们使用inject节点输入数组[0xd,0x80],由于inject节点里不支持直接输入十六进制的数字,所以输入[13,128]。这两个数组的值是一样的。

数值输入

函数节点进行如下修改:

函数修改

连线并部署,然后点击inject节点的输入按钮,可以看到调试窗口输出的正是我们期望的结果:34.56。

可以再添加一个节点,把数字34.56转为字符34.56摄氏度。

图形化展示

函数展示

使用context实现计数器

在之前的流中,我们总是在执行函数节点时新建一个变量。函数节点执行完毕以后,这个变量的值就会丢失,没有办法保存。如果需要保存一个变量的值该如何操作?

可以借助context对象。它可以理解为上下文,或者语境,用于保存内存中的数据,这个数据可以持续保存到下一个消息到来。Context可用于保存索引,计数或者消息中的数据。

拖入inject节点与debug节点。拖入一个函数节点,进行如下设置:

拖入节点进行设置

完成以后连线并部署,多次点击inject节点的输入按钮,并在调试窗口观察现象。

图形化展示

发现调试窗口中的数据在递增:

调试窗口数据

接下来结合现象分析代码:

    var count = context.get('count')||0;//如果count不存在就初始化为0,已存在则获取count的值    
    count += 1;                                              
    context.set('count',count);//执行完+1操作以后保存count的值                        
    msg.payload = count;                                      
    return msg;

我们可以查到Context的一些API(应用程序接口)如下:

context.get(..) : 获取一个节点范围内的上下文属性

context.set(..) : 设置一个节点范围内的上下文属性

context.keys(..) : 返回所有节点范围上下文属性键的列表

context.flow : 同 flow

context.global : 同lobal

每次的输入都不变,count每次都+1,最后输出递增的数,说明count记录了上一次输入的值。执行Context.get以后,就可以获取到之前保存的值。

执行context.set以后,就能把处理过后的值保存下来。保存到哪里?'count',带单引号的count里。接下来用流程图来表示逻辑。注意,带单引号的'count'与变量count不一样。

流程图

使用flow在不同节点之间传递参数

函数节点计数器实现了用'count'来保存数据的方法,那么这个'count'里边的内容,能否被别的节点使用?我们来尝试一下。 在上一章节的基础上进行修改,添加计数器2,并做如下修改:

添加计数器

编辑计数器

如果计数器2可以使用'count'的内容,那么payload里的内容应该是递增的数据。然而,部署以后,观察实验现象,可以发现OUT2的内容并没有变化。这说明计数器2没有办法获取'count'里的数据。

调试窗口数据打印

细心的读者可以发现,在context的API里,有这么一句解释:

context.get(..) : 获取一个节点范围内的上下文属性

也就是使用context属性只能影响一个节点范围内。然而在实际的应用中,不同的节点之间可能会用到同一个变量,也就是说,有一个"全局变量",该如何传递?

我们查阅了函数的API,找到了flow的context用法,地址是: https://nodered.org/docs/writing-functions

flow.get(..) : 获得流作用域上下文属性

flow.set(..) : 设置流作用域上下文属性

flow.keys(..) : 返回所有流作用域上下文属性键的列表 修改为"计数器"节点

修改计数器函数

修改"计数器2"节点:

修改计数器2节点

部署并点击inject节点的输入按钮,看到现象如下:

调试窗口数据打印

这说明计数器2获取到了计数器中的count值,也就是通过flow.set与flow.get实现了不同节点的参数传递。流程分析如下:

流程图

使用flow实现计时器

使用flow的属性在不同的节点之间传递参数,这个功能十分有用。接下来尝试使用flow实现一个节点执行时间的计数器。

在JS中,可以使用Data对象的getTime方法获取从1970年1月1日至今的毫秒数。跟时间戳得到的数据类似。在开始节点和结束节点分别获取时间,两个值的差就是这两个节点之间所用的时间了。

另外,需要一个延时节点来放在两个计时节点之间。在功能控件里恰好有一个名为"delay"的控件满足要求。它可以控制消息通过自己的时间或者限制它的传递速度。我们会使用它的随机延时的功能。

这次我们先来画一个流程图来分析思路。

思路流程图

这是开始时间的设置:

设置开始时间

这是结束时间的设置:

设置结束时间

这是delay节点的设置:

设置延时时间

整个流连线如下,完成以后部署并点击inject节点的输入按钮:

图形化展示

可以看到,delay节点出现了一个蓝色的标记,以及表示时间的数字。这就是节点的状态status。

随机延时

使用全局变量

全局变量名为global,它与flow功能类似,但是作用域比flow大,并且,即便重新部署,global储存的值也不会丢失。以下是global的API。

global.get(..) :获取全局范围的上下文属性

global.set(..) :设置全局范围的上下文属性

global.keys(..) :返回所有全局作用域上下文属性键的列表

流程图

整个流程如下设计:

图形化展示

开始时间与当前时间分别设计如下:

开始时间

当前时间

完成以后部署,并先点击开始时间的输入按钮,再点击当前时间的输入按钮。调试窗口现象如下:

调试窗口数据打印

从现象中可以看出来虽然两个流没有直接连接,但是第二个流仍然获取了在第一个流中设置的变量。说明实验成功。接下来,观察重新部署时,global变量能否保存。为节点做一些无关紧要的修改,例如out改为OUT,以便能够点击"部署"按钮。

图形化界面修改

部署完成以后,不要点击"开始时间"的输入按钮,直接点击"当前时间"的输入按钮,可以观察到如下的现象。

调试窗口数据打印

逝去的时间并没有被重置,说明重新部署不影响global的内容。

函数控件的其它功能

函数控件的功能强大,甚至可以用函数控件实现其它所有功能控件的功能。在函数空间中,除了可以调用JS自带的各种方法以外,含有一些node-red特有的API可以使用,把它写在这里:

node.id :函数节点的ID,在0.19版本添加

node.name :函数节点的名字,在0.19版本增加

node.log(..) : 记录一条消息

node.warn(..) : 记录一条警告消息

node.error(..) : 记录一条错误消息

node.debug(..) :记录一条调试消息

node.trace(..) : 记录跟踪消息

node.on(..) : 注册一个事件处理函数

node.status(..) : 更新节点的状态

node.send(..) : 发送消息

这其中从log到trace都是用来向命令换输出信息的,由于用法十分简单,就不讲解了。

使用send方法发送数据

前面遇到一个问题:如果"apple"既满足条件1,又满足条件3,但是条件1执行return 的操作以后,条件3就不再执行了,有没有既可以发送数据,又不会结束函数的方法?当然有。我们查询了节点可以调用的API,发现有send函数,满足我们的要求。 查一下之前的代码,把"判断主题"节点做如下修改:

判断主题

然后部署并观察现象,可以发现,当点击apple的输入按钮时,OUT1与OUT3都可以收到消息,说明实验成功。

数据打印

流程之间传递函数

Context元素除了可以传递变量以外,也可以传递函数。我们在流1中新建一个名为hello的函数,在流2中调用它。

流1的函数节点:

流1节点函数

流2的函数节点:

流2节点函数

先点击IN1的输入按钮,再点击IN2的输入按钮,可以看到OUT2中打印出了hello world,其中的"hello"是通过调用流1 的hello()函数打印出来的,说明实验成功了。

调试窗口打印

使用状态标志

第一次使用delay节点,用于产生一个随机数。此节点在在运行的时候显示出了一个表示时间的字符,显示这个随机的时间是多少。我觉得这个功能很有用,很直观的可以显示出我们感兴趣的一些数据或状态,可以省去到处用debug节点打印的烦恼。翻看函数节点的API,正好有一条可以用来显示状态: node.status(..) : 更新节点的状态

随机延时

我们来研究一下如何使用。

首先要搭建一个可变的输入信息,并且要持续一段时间,避免状态变得太快看不到。相信详细学过函数控件用法以后,对于聪明的你来说,这一定不是难事。——用函数节点+delay节点可以实现。先拖拽节点,形成数据流。

数据流

第一个函数节点用于发送5个数字:

send1-5节点函数

Delay节点用于限制数据包的速度,每秒一条。

Delay节点

如此就能实现,第二个函数节点每秒接收到一个数字。

节点函数

第二个函数节点的代码如下:

node.status({fill:"green",shape:"dot",text:"Received "+msg.payload});
return msg; 

也很好理解:填充绿色的点,文本显示"Received + 载荷",只是格式死板一点,照着填写,不要遗漏标点或拼写错误。

接下来点击inject的输入按钮,可以在调试窗口看到每隔一秒收到一个数据。这都是意料中的现象。

调试窗口打印

同时,第二个函数节点也能显示出一些信息。绿色的点,以及显示内容都是我们代码中自己编写的,所以,任务完成。

串口控件安装与介绍

串口控件是一个很有用的控件,它可以通过电脑或树莓派的串口收发数据。由于我们要做一个基于node-red的应用,并不是做一个只运行与电脑的纯软件,所以必然要与一定的硬件产生关联。这些硬件往往可以支持串口,所以,通过串口控件,可以把node-red程序控制的对象从电脑扩展到实际的硬件中,极大提升了node-red的实用性。

如果有嵌入式的开发经验,那么对于"上位机"这个名词一定不会陌生。把"下位机"采集到的数据通过串口发送给电脑或树莓派,然后用node-red搭建一个上位机真的是很简单的事情。如果没有听说过上位机,也没有关系。假设有这么一个设备,可以定时采集到一些传感器的数据,比如室内的温度,这些数据总得汇报给另外一个设备,以便进行显示和分析。负责显示和分析的设备,就可以是上位机。

串口控件安装

默认情况下,node-red并不会带有串口控件。我们可以进行手动安装。顺带提一下,node-red共有1000多种各式各样的控件,默认情况只会安装最常用的几十种。所有控件的安装过程都类似。

点击选项按钮,找到节点管理:

节点管理

切换到"安装"标签内,在输入框内输入serialport,找到node-red-node-serialport,点击安装。

点击安装

点击安装

安装完成以后,输入控件与输出控件区都会增加一个名为"serial"的控件。

增加控件

串口控件简介

串口输入控件说明

从本地串行端口读取数据,它可以用以下方式分割字符,等待"分割"字符(默认\n)。也接受hex表示法(0x0d)。

等待从第一个字符接收到的毫秒数,等待填充一个固定大小的缓冲区,然后输出msg.payload作为UTF8 ascii字符串或二进制Buffer对象。

Msg.port设置为被选择端口的名字。

如果没有指定分割字符,或者超时或缓冲区大小为0,那么就会发送单个字符的流——要么作为ascii字符,要么是1位二进制缓冲区。

串口输出控件说明

提供到输出串口的连接,只发送msg.payload,可以选择吧用于分割输入的新行字符追加到发送到串口的每条消息。

二进制的载荷可以使用Buffer对象发送

使用串口助手与虚拟串口工具

串口助手软件有很多,推荐使用SSCOM,串口/网络数据调试器,下载地址是https://www.daxia.com/sscom/sscom5.13.1.rar

虚拟串口工具可以使用VSPD。由于版权问题,请自行搜索下载地址。该软件可能需要破解,请支持正版。

串口/网络数据调试器可以把串口收到的数据显示出来,也可以通过串口发送数据,十分直观,可以用与显示串口控件的数据,和给串口控件发送数据。但是,一般来说,同一个串口只允许一个软件占用。例如串口控件占用了COM10(串口2),那么串口/网络数据调试器就不能使用COM10了。如果COM10可以与COM11进行通信,而串口/网络数据调试器使用COM11,那么串口/网络数据调试器就可以与串口控件通信。虚拟串口工具可以把COM10与COM11连接起来,也就是借助虚拟串口工具,可以在不需要任何硬件的情况下,可以用串口/网络数据调试器模拟真实的串口,与串口控件通信。连接情况可以下图说明。

串口控件通信

虚拟串口工具设置

打开虚拟串口工具:

打开虚拟工具

在Manage ports下,添加端口的地方改为COM9与COM10连接。也可任意填写其它串口号。

填写串口号

完成添加以后左侧的virtual ports显示新添加的串口。注意,两个串口设置为连接以后,即便关闭程序,串口的连接仍然保持,所以建议使用比较大的串口号,其它设备用不到的。如果需要解除连接,可以点击删除端口或是重置端口。

串口/网络数据调试器的介绍

串口/网络数据调试器功能强大,使用方便,基本可以通过自己的摸索学会使用。为了照顾无基础的读者,我这里还是把常用的功能罗列一下,有基础的可以跳过这一小节。

  1. 选择端口号
    可以看到端口号下边已经有了通过虚拟串口工具仿真出来的串口。除了串口以外,还有TCP和UDP的功能。 选择串口号
  2. 波特率与更多串口设置
    波特率代表数据发送的速率(每秒发送多少位数据),发送方与接收方必须保持一致。
    选择波特率
    有时串口通信还需要配置一些其它参数,如数据位,校验位,控制位等,默认是8个数据位,没有校验位,1个停止位。这种设置简称为8N1。
    配置其他参数
  3. 扩展工具
    SSCOM由于拥有扩展工具,所以十分方便,简直可以作为一个简单的上位机来用。
    扩展工具
    点击扩展工具以后可以看到一个新的工具栏。可在输入框输入需要发送的信息,双击输入框可以修改注释。可以勾选是否使用hex发送,以及按顺序定时发送。
    预存发送数据

串口控件详细介绍

hello serial

跟"hello world"类似,,虚拟串口工具与串口助手都设置完毕以后,我们先用一个最简单的程序检验下环境搭建情况如何。首先使用串口助手打开COM11,并设置为"非HEX显示", "非HEX发送"。

勾选选项

然后在node-red工作区里添加以下几个控件:

添加控件

设置inject输出文字"hello serial":

设置输出文字

两个串口控件都设置如下:

串口控件设置

serial-port配置

然后连线并部署:

连线及部署

点击inject的输入按钮,可以在串口工具看到"send serialport"。在串口工具的输入栏输入"hello seriport",也可以在调试窗口看到此信息。

查看调试信息

Node-red基于JS,这是前端技术的"三驾马车"之一。前端技术主要负责界面呈现,与用户交互等等,很多炫酷的特效都是前端呈现的。Node-red技术有这样的"基因",界面当然不会差劲。它只需要一个控件就可以实现一个页面。由于本书讲述的应用与页面关系不大,就不讲HTTP与websocket相关的控件了。强烈建议感兴趣的读者自己研究一下。

Node-red支持自定义节点,当然也就支持自定义图形化的节点。也有优秀的开发者把自己建立的图形化节点无偿分享。这里给出一个股票界面的例子,让大家看一看优秀的node-red界面能做到什么样子。

界面展示

仪表板安装与介绍

仪表板的安装

我们常用的图形化节点叫做仪表板,dashboard,也能做出效果不错的界面,例如:

仪表板安装

Dashboard还有一些其它控件,例如:

更多控件

仪表板的安装与串口类似,在安装节点的输入框内输入"dashboard",找到名为"node-red-dashborad"的控件并点击安装即可。

安装node-red-dashborad控件

安装完以后在屏幕左边会多出很多新的控件:

更多控件

检验安装结果

我们从中随便选择一个控件来检验安装好的控件是否能够使用。在deshboard的控件区找到"date picker"控件,拖入工作区,然后双击节点,点击Group输入框后边的编辑按钮,如图所示,把Group节点与Tab节点分别命名为Group与Tab。

点击编辑

重命名节点

重命名完成

拖入一个debug节点并连线部署。在浏览器中输入地址:http://localhost:1880/ui ,能正确打开此网页,已经可以说明安装成功了。然后可以看到名为date的一行内容。

安装成功

仪表板简介

在已经使用过仪表板以后再来看它的简介,不容易被弄糊涂。

dashboard主要用于快速创建实时数据仪表板。它需要node-red版本为0.14或更高。

仪表板的布局依赖于Tab和Group属性。Tab可以理解为页面,Group是分组。Tab可以包含Group。我们在选择"北京开幕式"那天的图片里,就有一个名为Group的分组。每个组的元素默认宽度是6个单位。每个单位默认宽度是48PX,间距6PX。一个页面里可以有多个分组,建议使用多个分组,而不是一个大组。因为node-red可以根据页面的大小动态调整分组的位置。

使用dashboard节点时,屏幕右侧"调试窗口"的旁边会多一个名为dashborad的小标签,下边有Layout,Theme和Site三个选项。

Layout意思是布局,在Layout里可以重新排列Tab,Group与控件,并编辑其属性。也可以把其它网页添加到Tab中。

Theme意思是主题。可以选明亮的,或者暗的,或者自定义。

Site意为地址,可以设置标题的UI,或者选择标题栏。也能够以像素为单位设置网格布局的基本图形,就是刚刚提到默认是48像素的那个"单位",或者单独设置控件,组的大小。

点击dashboard

选择Style

dashboard节点配置

Dashboard的控件一般都可以设置Label或名字,Label和名字也可以通过传入消息的属性来指定或修改。其实控件的内容都可以用消息来配置,这属于比较高阶的用法,感兴趣的可以参考:https://github.com/node-red/node-red-dashboard/blob/master/config-fields.md 。 Dashboard带有的控件简介见下表:

名称

中文名

详细信息

button

按钮

可以点击,发送一个msg

dropdown

下拉菜单

可以指定标签与值的对应关系

switch

开关

添加一个开关到用户界面,开关变化的切换可以产生一个msg

slider

滑块

水平滑块,可变步长

numeric

数字选择

带有上、下按钮的数字输入控件

text

input

文本输入

date picker

日期选择器

用于选择日期

colour picker

拾色器

用于选择颜色

form

表单

一个可由多个子小部件组成的小部件。提交时,所有值均作为单个消息提交

text

文本

一个只读控件,可以配置Label和Value

gauge

仪表

有四种模式,标准,甜甜圈,指南针和波形。可以指定标准仪表或甜甜圈测量仪的颜色范围

chart

图表

具有折线、条形与饼图模式,可以使用日期格式化程序字符串来自定义X轴标签

audio out

音频输出

用于播放音频或发送文本到语音客户端

notification

通知

为用户创建警报,可以是弹出窗口或是报警框

ui control

UI控制

允许对仪表板进行动态控制

template

模板

模板节点允许用户使用HTML,Javascript在框架内创建自己的小部件

常见的输入型仪表板控件应用

从仪表板的简介表中可以看出,仪表板控件的类型可以分为两种:一种是输入型,功能类似于inject控件,用于生成msg。另一种是显示型,或者说是输出型,与debug控件类似,用于展示某些数据。本章节只讲述输入型的控件。

按钮button简介

按钮的帮助信息

向用户界面添加一个按钮。单击该按钮会生成带有载荷的消息。如果没有指定载荷,则使用节点id。按钮的图标、文字、背景都可以自定义,也可以使用消息属性来设置,例如msg.background,msg.topic。还可以使用 msg.enabled来使能或禁用按钮。使用msg来设置的方法要用到函数节点,比如在函数节点内使用代码设置msg.background为"black",并返回msg。此处对于使用函数来设置控件的方法不做讲解。

在工作区拖入一个按钮节点,并把Tab和Group分别设置为home和control。

找到dashboard tab节点设置

设置Tab

设置Group

再拖入一个debug节点,连线并部署:

连线并部署debug节点

在(http://localhost:1880/ui)页面下就可以看到刚刚设置的的按钮,点击按钮,可以在调试窗口看到以下信息。

点击按钮

调试窗口打印数据

这说明,点击按钮以后,按钮可以给流注入一个数据包。

使用按钮实现简易电话拨号界面

我们在编辑按钮节点时,其实有很多选项都空着,例如尺寸、图标、标签、颜色等等。合理使用这些选项,即便是只用按钮控件也能做出来一些有意思的东西,例如做一个简易的电话拨号界面。

拨号界面

接下来结合这和拨号界面,来讲讲我们设置了哪些参数。以下是button节点可以设置的参数。

设置button节点

首先,很直观可以看到,数字的背景是不一样的,123的背景是灰色,456的背景是浅蓝,所以123控件的Background处应当填写"gray"。

填写gray

按键的大小也是不一样的。一般来说,按键的大小默认是自动,也就是长度跟随Group,宽度是1。Group的宽度是多少呢?默认是6,可以修改,拨号界面就改成了3。修改的界面在屏幕右侧的dashboard界面下:

修改按键大小

设置宽度为3

Width就是宽度。顺带提一下,下边的两个选项分别是显示组的名字,和允许收缩。效果就是这样的:

选项

显而易见,普通数字按钮的宽度都是1,拨号键的宽度是2。

还有文字的颜色不一样。这个就很简单了,修改colour即可,比如数字的文字都是black。

修改文字颜色

另外,还可以看出,拨号的按键上有一个小小的电话图标;按键0的大小好像比别的数字也大一点,这其实也是一个图标。图标如何设置?

在别的软件里,设置图标可能需要一个本地的图片。Node-red使用的是一个在线的图标库(不联网的时候显然没办法使用),地址是(https://material.io/tools/icons/?style=baseline)

这个网页里有很多图标,我截取一些放在这里:

每个图标下边都有名字,如果你需要使用这个图标,把名字输入到Icon后边即可,例如按键0的图标:

修改按键图片

既然是电话拨号按键,当然要有拨号的功能。我们让每一个数字按键被按下去的时候,都可以把数字发送到流里边去;再按下"CALL"的时候,就把之前按下的数字全部打印出来,暂时用debug节点来显示。

分析以后,发现需要一个函数节点利用context功能保存之前按下的数字,等到收到CALL的命令以后,一次性打印出来。函数节点的程序如下:

    var temp = context.get('num')||"";              
    if (msg.topic != "call"){                        
        temp = temp + ""+msg.payload;          
        context.set('num',temp);                  
    }
    else{                                         
        msg.payload = temp;                    
        return msg;                             
    }

如图设置流:

设置流

如果在仪表板的界面,有数字的顺序是颠倒的,可以在屏幕右侧dashboard页面下通过拖拽节点调整顺序:

调整数字顺序

我们来"拨打"电话,借以观察现象:按下数字按键时,并不会显示单个数字;按下CALL以后,一串电话号码都能显示出来。

调试窗口打印

开关switch的使用

Dashboard中的switch可以增加一个开关到用户界面。

开关状态的每一个变化都会产生一个带有on或off值的msg.payload。

开关的颜色与图标是可以配置的,也可以由传入的msg更新,方法跟button类似。

之前已经用过函数控件switch了,当时用"岔路口"来比喻那个函数控件。仪表板中的switch是真正的开关,功能上像是一个具备"开"与"关"两种状态的按钮。

直接拖入一个switch节点,修改它的Group和Tab,并与debug节点连接,部署。

编辑switch节点

部署switch节点

http://localhost:1880/ui/ 下可以看到这个开关,点击开关可以看到调试窗口下输出了true。

效果展示

调试窗口输出

其中的true与false当然也可以改为别的载荷,跟inject节点类似:

效果展示

调试窗口打印

滑块slider的使用

手机在调节屏幕亮度的时候,常常出现一个横线,串着一个圆球,拖拽圆球就可以改变屏幕亮度。横线加圆球组成的就是一个滑块。看上去有点像被吃的只剩下一个的糖葫芦,或者就叫做滑球更贴切。

Dashboard中的滑块可以帮助用户在最小值与最大值的范围内,改变设置的值。每次变化都会把值作为消息的载荷。

拖入一个slider控件,修改Group与Tab,然后与debug节点连接,部署。

滑块形状如下:

滑块形状

拖动滑块,可以在调试窗口见到当前滑块的值。

滑块值

注意,假如需要从0到3,那么拖动滑块时,1和2的值也会发送。可以直接点击到3的位置。默认情况下,滑块的范围是从0到10,步进1,也可以修改,例如10到80,步进2。

滑块值

输入框text input的使用

Text input可以向用户界面添加一个文本输入区域,格式可以是常规文本、电子邮件或颜色选择器等。

输入都以msg.payload的形式发送。也可以通过输入msg的载荷来预设输入的文本。

Delay参数可以设置从输入字符到发送的延时,默认是300毫秒。也可以设置为0,等待按下"Enter"或"Tab"键发送。

电子邮件模式可以使用红色来表示无用的地址。

时间输入类型返回从午夜开始的毫秒值。

接下来通过实践来验证下text input的功能。

拖入一个text input节点,修改Group与Tab,然后与debug节点连接,并部署。

滑块值

输入任意的文字,在调试窗口可以看到。

滑块值

滑块值

所以我建议把输入的延时设置为0,用"Enter"和"Tab"作为结束的标记。 再来试一试输入框的别的功能,比如密码:

滑块值

我们把Label也写上,在仪表板的页面可用于显示一些提示。

滑块值

在"number"的模式下,没办法输入其它的字符。

滑块值

滑块值

使用文本输入的取色功能为按钮设置颜色

文本输入的模式里有颜色选择,可以调出一个取色板,并返回颜色的编码。我们在讲述按钮的时候,说过可以使用msg来设置按钮,这里正好来把两者结合一下。

首先,我们把text input的模式设置为picker,并用debug节点来看一看选中颜色以后会输出什么。

滑块值

滑块值

点击仪表板页面的颜色输入框,可以看到这样的一个取色板:

滑块值

我们随便选一个颜色并点击确定。比如红色。再来看调试窗口的信息。

滑块值

收到消息是#004080,这就是蓝色的编号。我们也可以在设置颜色的页面用这种方式。用颜色选择器看颜色的编码,再进行颜色设置的办法很方便。

下拉菜单dropdown的使用

下拉菜单是输入框与按钮的结合体,常用于有指定选项的输入。例如,您住在哪个省?这个答案就可以用下拉菜单,只不过选型可能会多一点。但是可以避免用户输入信息,也可以避免他犯错。接下来就使用下拉菜单来做一个简单的例子。

俗话说,"桃三杏四李五年,要吃苹果等八年,枣子当年能卖钱"。这句话里包含5个键值对,也就是关键字与数值的对应,及桃对应5年......枣对应1年,我们使用下拉菜单来完成这样的任务:选择某个水果,就输出对应的年份。

拖入一个dropdown节点,如图进行配置:

滑块值

再拖入一个text节点。注意不是text input。我们以前一直使用debug节点来显示信息,现在想把信息显示在仪表板的页面上,就用这么个节点。然后进行如下修改:

滑块值

连线并部署:

滑块值

在仪表板的页面,就可以看到这样的页面。点击下拉菜单,就可以看到候选框。

滑块值

滑块值

滑块值

常见的显示型仪表板控件应用

仪表gauge的使用

向用户界面添加一个仪表类型小部件。

输入的msg.payload应该是数值,且格式要与Value Format一致。Value Format是gauge中的一个选项,具体情况参考https://scotch.io/tutorials/all-about-the-built-in-angularjs-filters 可以指定3个扇区的颜色,并将其混合在一起,颜色也可以由#RRGGBB来表示,这个表示方法前边已经用过了。

仪表板有四种模式,常规的,甜甜圈,指南针和波浪。

Label可以用消息的属性来设置。

我们直接拖入4个gauge,来看看不同的"Type"有什么区别。为了方便比较,直接让Type与Label的名字一样。

滑块值

然后拖入一个滑块作为输入,为仪表提供数据。为了方便显示,需要给仪表设置不同的group,例如display1和display2。部署以后拖动slider,可以看到仪表上有实时的变化。

滑块值

滑块值

图表chart的使用

将输入值绘制在图表上。这可以是基于时间的折线图,条形图(垂直或水平),或者饼图。

每个输入msg.payload值将被转换成一个数字。如果转换失败,则消息将被忽略。

最小和最大Y轴值是可选的。如果没有设置最值,该图表的范围将根据接收的值自行扩展。

使用不同的msg.topic可以在同一个图表上显示不同的多个连续数据。同一串连续的数据也可以用msg.label来显示不同的小节。

X轴定义了一个时间窗口或显示的最大点数。较老的数据将自动从图中删除。轴的坐标名称可以用特定的格式进行格式化。

输入的msg.payload如果包含空数组的话,会清空表格。

参考此网站,可以学会如何把预先准备好的数据格式化为完整的图表。https://github.com/node-red/node-red-dashboard/blob/master/Charts.md 跟仪表类似,我们直接新建6个图表,用来显示不同类型的图表。然后使用3个slider作为输入的数据源。

滑块值

滑块值

拖动滑块可以直观看到各种表格的现象,也就很容易能区分出它们的作用。比如chart就是折线图,bar chart就是柱状图,bar chart h就是水平的柱状图等等。

特别说明一下,折线图只保留了1分钟或是30个数据。保存的数据多了可能会卡顿。

滑块值

另外除了折线图,我都设置了图表的范围。由于部署前后,并不会清除之前已经保存的数据。如果出现了删除不掉的数据,可以关闭浏览器与powershell,重新启动node-red。

通知notification的使用

在用户界面上,把msg.payload作为一个弹出的通知,或是有OK和Cancel的对话框信息。

如果msg.topic是可用的,会被用作通知的标题。

如果你没有设置一个可选的边界高亮显示颜色,那么可以用msg.highlight属性来设置。

通知的位置和持续的时间也可以设置。

对话框会返回一个msg.payload字符串,内容是你配置的按钮Label。按钮的内容是可选的。

接下来把notification和dialog的功能都验证一下。

滑块值

我们使用按钮来产生消息,消息包含msg.topic和msg.payload。用text来显示对话框的结果。按下notification后现象如下:

滑块值

按下dialog以后有对话框如下:

滑块值

答案当然是帅了,点击帅,结果如下:

滑块值

Notification节点可以设置弹窗通知的时间和位置,也可以自定义主题。

滑块值

在text中,我们也是用了一个新的用法:拼接字符串。

滑块值

至此,仪表板的使用就告一段落了,相信你已经可以按照自己的想法搭建出一个不错的图形化显示和控制界面。如果已有的控件达不到你的要求,可以自己研究下UI control 和template。

TCP控件

TCP控件简介

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。不论是在计算机网络OSI模型中,还是在因特网协议族(Internet protocol suite)中,TCP层都可以发送用于网间传输的、用8位字节表示的数据流,并把数据流分区成适当长度的报文段。之后TCP把结果包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。TCP用一个校验函数来检验数据是否有错误;在发送和接收时都要计算校验和。

简单来说,TCP提供了不同设备之间的网络连接。在我们的设备中,树莓派与电脑通过TCP网络进行数据的交换。本节我们就来认识一下TCP控件。 TCP输入节点:

图片

提供了TCP输入的选择。可以连接到远程TCP端口,也可以接收传入的连接。

TCP的输出提供TCP输出的选择。可以连接到远程TCP端口,接受传入的连接,或者回复从节点上的TCP接收到的消息。

只发送msg.payload。

如果msg.payload是一个包含二进制数据的Base64编码的字符串,Base64解码选项将使它在发送之前被转换回二进制。

如果msg._session(会话)不存在,payload会发送给所有连接的从机(或者说客户端)。

注意:在某些系统上,您可能需要root或管理员访问,以访问1024以下的端口。

我们的TCP案例使用C/S结构。即Client/Server (客户端/服务器) 结构,是大家熟知的软件系统体系结构,通过将任务合理分配到Client端和Server端,降低了系统的通讯开销。

客户端和服务器的程序不同,用户的程序主要在客户端,服务器端主要提供数据管理、数据共享、数据及系统维护和并发控制等,客户端程序主要完成用户的具体的业务。

作为server的时候必须配置端口。

作为client的时候需指明server的IP和端口。

TCP控件作为客户端

讲解时,为了方便,使用串口助手的网络功能来模拟另一个设备。需要指明的是,串口助手虽然运行在电脑上,但是可以虚拟出一个IP,这个IP并不是电脑的物理IP,而是虚拟的,因此可以认为,node-red与串口助手虽然在同一台电脑上运行,但是IP是不一样的,因此也就是2个不同的设备。

串口助手的端口号选择TCPServer,随意填写一个本地IP和端口,并点击侦听。

图片

Node-red里我们实现一个应声虫的程序,即收到什么,就发什么。TCP in与TCP out节点各拖入一个。然后进行如下设置:

图片

类型选择连接,表示node-red里运行的是客户端。客户端需要指明服务器的IP和端口,要与串口助手的设置一样。设置完成以后,连线并部署。如果程序没有问题,可以看到TCP控件显示已连接。

图片

在串口助手的输入框内输入任意内容,并点击发送,可以看到接收框内显示收到的信息,与发出的信息内容完全一样。

图片

在node-red内的调试窗口,可以看到debug节点打印出的调试信息内容也正确:

图片

说明:node-red中的TCP控件作为客户端的通信成功了。特别说明一下,在C/S结构中,一个服务器可以对应多个客户端,即便是有多个客户端连接一个服务器,通信也是可以成功的。

TCP控件作为服务器

Node-red内的TCP控件可以作为服务器。但是不同于上一节的串口助手,TCP控件没有办法设置IP地址,因此只能使用电脑的物理IP。

在window平台下,可以在控制面板中找到网络和共享中心,点击连接的网络,一般是以太网,然后点击详细信息,就可以看到IPv4的地址。

图片

上图,我的IPv4的地址是192.168.1.163 串口助手里的端口号选择TCPClient,远程的IP与电脑对应,本地IP可以随意填写,与远程不同即可。端口号要与node-red的程序对应。

图片

Node-red里我们仍然编写应声虫的程序。拖入新的TCP in与TCP out节点,由于同一个IP与端口下只能有一个服务器,因此两个节点分别如下设置:

图片

图片

连线并部署:

图片

串口助手中点击连接,并发送任意内容,可以看到接收框内显示出收到的信息,与发出的信息内容完全一样。

图片

Node-red里的调试窗口也显示出刚刚收到的消息,说明实验成功。

图片

此时,再打开一个串口助手,正确配置为client以后,也可以实现这些功能。因为一个服务器可以对应多个客户端。

变量和变量赋值

变量声明如下:

var myFirstVar;

声明可以由逗号分隔在同一行:

var myFirstVar, mySecondVar;

它们被指派一些初始值使用=操作符。

var myFirstVar = 0;

对象

Date对象用于处理日期和时间。

创建Date对象的语法:

var myDate = new Date();//Date对象会自动把当前日期和时间保存为其初始值。

Date对象属性:

Constructor :返回对创建此对象的Date函数的引用

Prototype : 使您有能力向对象添加属性和方法

Date对象的方法:

方法

描述

Date()

返回当日的日期和时间

getDate()

从 Date 对象返回一个月中的某一天 (1 ~ 31)

getDay()

从 Date 对象返回一周中的某一天 (0 ~ 6)

getMonth()

从 Date 对象返回月份 (0 ~ 11)

getFullYear()

从 Date 对象以四位数字返回年份

getYear()

请使用 getFullYear() 方法代替

getHours()

返回 Date 对象的小时 (0 ~ 23)

getMinutes()

返回 Date 对象的分钟 (0 ~ 59)

getSeconds()

返回 Date 对象的秒数 (0 ~ 59)

getMilliseconds()

返回 Date 对象的毫秒(0 ~ 999)

getTime()

返回 1970 年 1 月 1 日至今的毫秒数

getTimezoneOffset()

返回本地时间与格林威治标准时间 (GMT) 的分钟差

getUTCDate()

根据世界时从 Date 对象返回月中的一天 (1 ~ 31)

getUTCDay()

根据世界时从 Date 对象返回周中的一天 (0 ~ 6)

getUTCMonth()

根据世界时从 Date 对象返回月份 (0 ~ 11)

getUTCFullYear()

根据世界时从 Date 对象返回四位数的年份

getUTCHours()

根据世界时返回 Date 对象的小时 (0 ~ 23)

getUTCMinutes()

根据世界时返回 Date 对象的分钟 (0 ~ 59)

getUTCSeconds()

根据世界时返回 Date 对象的秒钟 (0 ~ 59)

getUTCMilliseconds()

根据世界时返回 Date 对象的毫秒(0 ~ 999)

parse()

返回1970年1月1日午夜到指定日期(字符串)的毫秒数

setDate()

设置 Date 对象中月的某一天 (1 ~ 31)

setMonth()

设置 Date 对象中月份 (0 ~ 11)

setFullYear()

设置 Date 对象中的年份(四位数字)

setYear()

请使用 setFullYear() 方法代替

setHours()

设置 Date 对象中的小时 (0 ~ 23)

setMinutes()

设置 Date 对象中的分钟 (0 ~ 59)

setSeconds()

设置 Date 对象中的秒钟 (0 ~ 59)

setMilliseconds()

设置 Date 对象中的毫秒 (0 ~ 999)

setTime()

以毫秒设置 Date 对象

setUTCDate()

根据世界时设置 Date 对象中月份的一天 (1 ~ 31)

setUTCMonth()

根据世界时设置 Date 对象中的月份 (0 ~ 11)

setUTCFullYear()

根据世界时设置 Date 对象中的年份(四位数字)

setUTCHours()

根据世界时设置 Date 对象中的小时 (0 ~ 23)

setUTCMinutes()

根据世界时设置 Date 对象中的分钟 (0 ~ 59)

setUTCSeconds()

根据世界时设置 Date 对象中的秒钟 (0 ~ 59)

setUTCMilliseconds()

根据世界时设置 Date 对象中的毫秒 (0 ~ 999)

toSource()

返回该对象的源代码

toString()

把 Date 对象转换为字符串

toTimeString()

把 Date 对象的时间部分转换为字符串

toDateString()

把 Date 对象的日期部分转换为字符串

toGMTString()

请使用 toUTCString() 方法代替

toUTCString()

根据世界时,把 Date 对象转换为字符串

toLocaleString()

根据本地时间格式,把 Date 对象转换为字符串

toLocaleTimeString()

根据本地时间格式,把 Date 对象的时间部分转换为字符串

toLocaleDateString()

根据本地时间格式,把 Date 对象的日期部分转换为字符串

UTC()

根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数

valueOf()

返回 Date 对象的原始值

Array对象:

创建Array数组对象的语法:

New Array();
New array(size);
New array(element0,element1,.......,element);
参数:

参数 size 是期望的数组元素个数。返回的数组,length 字段将被设为 size 的值。

参数 element ..., elementn 是参数列表。当使用这些参数来调用构造函数 Array() 时,新创建的数组的元素就会被初始化为这些值。它的 length 字段也会被设置为参数的个数。

返回值:

返回新创建并被初始化了的数组。

如果调用构造函数 Array() 时没有使用参数,那么返回的数组为空,length 字段为 0。

当调用构造函数时只传递给它一个数字参数,该构造函数将返回具有指定个数、元素为 undefined 的数组。

当其他参数调用 Array() 时,该构造函数将用参数指定的值初始化数组。

当把构造函数作为函数调用,不使用 new 运算符时,它的行为与使用 new 运算符调用它时的行为完全一样。

Array对象方法:

方法

描述

concat()

连接两个或更多的数组,并返回结果

join()

把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔

pop()

删除并返回数组的最后一个元素

push()

向数组的末尾添加一个或更多元素,并返回新的长度

reverse()

颠倒数组中元素的顺序

shift()

删除并返回数组的第一个元素

slice()

从某个已有的数组返回选定的元素

sort()

对数组的元素进行排序

splice()

删除元素,并向数组添加新元素

toSource()

返回该对象的源代码

toString()

把数组转换为字符串,并返回结果

toLocaleString()

把数组转换为本地数组,并返回结果

unshift()

向数组的开头添加一个或更多元素,并返回新的长度

valueOf()

返回数组对象的原始值

类型

JavaScript支持各种类型如下:

JavaScript支持的基本类型

var anInt = 0;//int类型
var aFloat = 2.5;//浮点型
var aBoolean = true;//布尔型(true, false)
var aString = "This is a string";//支付串
var anArray =(1、2、3);//数组
var anObjectLiteral =
{
someField :"Some value"
};
Var aFunction= function(){返回;};//函数
var re = / ab + c /;/ /正则表达式
可以使用这样的新运算符实例化对象:

var aDate = new Date();

注释

注释可以使用双斜杠单行或c风格的多行注释的格式:

//这是一行注释

/*

这是一个多行注释23. */

基本运算符

内置的数值运算符包括通常的:+(加),(减法),/(部门)*(乘法),%(模)。

var a = 6;
a= + 1;//a是7
b = b % 2;//b是3
a+ +;//a是8
a-;//a是7
a+ = 3;//a是10

字符串可以连接如下所示:

var aString = "The value of a is " + a;
var greeting = "What a very ";
greeting += "nice day";
typeofoperator返回一个变量的类型。

typeof aString;//返回"字符串"
确定对象的类,例如区分数组和其他对象,对象。toStringmethod可以使用如下:

Object.prototype.toString.call(new Date());//"data对象"
Object.prototype.toString.call([1,2,3]);// "(对象数组)
Object.prototype.toString.call({someField:12});//"对象对象"
Object.prototype.toString.call(function(){return;});// "(目标函数)

toString方法

toString() 方法可把数组转换为字符串,并返回结果。

语法: ArrayObject.toString()
返回值: ArrayObject 的字符串表示。返回值与没有参数的 join() 方法返回的字符串相同。 实例:

Var arr = new array(3)                                                                          
Arr[0] = "George"                                                                          
Arr[1] = "John"                                                                          
Arr[2] = "Thomas"                                                                          
Document.write(arr.toString())

输出: George,John,Thomas

条件

您可以使用比较运算符,<(小于)>(大于)< =(小于或等于)> =(大于或等于)= =(等于)! =(不等于)ifstatements如下:

var = 1;                                                                          
var b =null;(null为空)                                                                          
if(a> 0){                                                                          
b ="大于零";                                                                          
}else {                                                                          
b ="是零或更少";44. }  

循环

avascript支持for循环 and while循环:

for (var i= 0 ; i< anArray.length; i + +)                                                                           
{
    
console.log(myArray[i]);

}                                                                              
while(x<10)                                                                          
{

 console.log(x + +);52.
 
}    

函数

1. 创建函数

调用一个函数,你可以调用如下:

function addOne(x){                                                                          
return x+1                                                                          
}                                                                          
var y = addOne(5);/ / y = 6 

或者,如果函数是对象的一个方法,例如一个日期对象:

var d = new Date();                                                 var year= d.getMonth();//一月为0,二月为一 

2. parseInt()

parseInt()函数可解析一个字符串,并返回一个整数。创建paseInt函数:

parseInt(String , radix);

String :必须,要被解析的字符串。

Radix:可选。表示要解析的数字的基数。该值介于2~36之间。如果省略该参数或其值为0,则数字将以10为基础来解析。如果它以"0x"或"0X"开头,将以16为基数。如果该参数小于2或者大于36,则parseInt()将返回NaN。例如:

parseInt("10");//返回10                                                                          
parseInt("19",10);//返回19(10+9)

3. parse()

parse方法可解析一个日期时间字符串,并返回 1970/1/1 午夜距离该日期时间的毫秒数。

语法:

Date.parse(datestring);
参数:

Datestring : 必须。表示日期和时间的字符串。

返回值:

指定的日期和时间剧19701/1午夜(GMT时间)之间的毫秒数。

说明:

该方法是 Date 对象的静态方法。一般采用 Date.parse() 的形式来调用,而不是通过 dateobject.parse() 调用该方法。

实例:

在本例中,我们将取得从1970/01/01到2005/07/08的毫秒数:

var d = Date.parse("Jul 8, 2005")                              
document.write(d)

输出: 1120752000000

回调

node.Js是一个基于事件的框架,只有一个执行线程。要允许在执行i/o或等待计时器过期时执行多个任务,将广泛使用回调。例如,要在2秒钟后将"完成"登录到控制台,可以使用匿名函数回调如下:

setTimeout(函数(){ console.log("做");},2000);                                                 ```

可以使用函数来处理数组:
```c
var friends = ["mike","roberto", "rodger", "ted", "daniel"];//实例化一个朋友数组
friends.forEach(function (eachName,index){//循环遍历名字                                                               
console.log (index + 1 +"."+ eachName);/ / 1. mike 2. roberto 3. rodger 4. ted 5. daniel 72. });

异常处理

异常处理代码中可以用来捕获错误。这样做,你的代码封装在一个try / catch块。

try {                                                                            
//try to do something需要处理的代码                                                                        
}catch (e){                                                                        
//handle errors 捕获的错误                                                                        
}finally {                                                                        
//this block is always executed regardless of whether there was an exception无论是否有异常,这个块总是执行。                                                                        
} 

Context (上下文)

JavaScript中的"上下文" 指的是什么:

上下文是从英文context翻译过来,指的是一种环境。

在软件工程中,上下文是一种属性的有序序列,他们为驻留在环境内的对象定义环境。

在对象的激活过程中创建上下文,对象被配置为要求某些自动服务,如同步,事务,实时激活,安全性等等。又比如计算机技术中,相对于进程而言,上下文就是进程执行时的环境。

具体来说就是各个变量和数据,包括所有的寄存器变量,进程打开的文件,内存信息等。

JavaScript的执行上下文的理解是一种大概模糊的理解。 上下文原意是content, Content指的是函数被调用的时候,查看this指向那个object,那么那个object就是当前的"上下文"。

总之我的理解就是:当前执行环境的作用域,因为"上下文"如果离开了执行环境就没有啥实际意义了。