版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、LabVIEW程序设计模式,这个相对学术化的词语是对一系列用于LabVIEW程序设计结构的归纳和总结。在建造房子时,需要针对房子的用途设计整个房屋的架构,确保房子在这个架构上的坚固性和可建造性。写程序时同样如此,不同的应用需要使用不同的程序设计结构。例如我们在LabVIEW中构建一个用户界面型程序时,往往首先在背面板中加入一个大的while循环以使程序持续运行。如果需要响应用户界面事件则还需要加入一个Event事件结构。那么我们是否曾经考虑过以下的这些问题:(1) 应用中是否存在并行响应的情况?如在持续的数据采集过程中,是否需要同时响应
2、单击菜单的事件?(2) 底层获取的数据如何与上层的数据显示部分进行数据交互?(3) 上层的界面如何受底层程序的控制?(4) 同一个循环中采用哪种方式进行数据交换?是局域变量、全局变量、共享变量还是移位寄存器?(5) 程序是否具有可扩展性?(6) 如果程序运行过程中,发生系统错误或者硬件通讯错误,是否会停止运行
3、?待错误排除后是否会继续运行?(7) 如何组织程序中的核心数据结构?是否需要采用面向对象程序设计?(8) 如何记录测试数据并生成报表?如何保存用户配置参数?(9) 如何处理程序运行中的断电情况?重新启动时的继续运行?数据的最低丢失?(10) 如何实现运行过程的采样触发和多点采样的同步?当然,也许只是使用LabVIEW临时地调试或开发某个小的应用,无需考虑上述的问题。但是,如果使用LabVIEW开发一个典型应用的程
4、序却无法回避这些问题。因此,有必要对各种程序开发的应用进行归纳和总结,提取它们对应的LabVIEW程序结构中的共性。此外,针对这些共性研究哪种结构更加适合于应用。这些结论综合起来就形成了程序设计的模式。对于初学者而言,理解和掌握程序设计模式往往能起到事半功倍的效果;而对高级用户而言,归纳各种程序设计模式又能够不断完善程序中遇到的问题,并衍生一套符合特定应用的特有的程序设计模式。状态机是一种最为经典的程序设计模式,在LabVIEW 7.1(含)之前它几乎统治了大部分的LabVIEW主程序。最基本的状态机结构如图 1所示。状态是状态机运行的经脉,在开始使用状态机模式撰写程序时需要将应用分
5、为若干个状态。下面以图中的应用为例说明基本状态机的使用。【应用1】前面板具有3个按钮(Control)和1个波形显示控件Chart(Indicator),功能分别是:1) 开始采集:Label是start,单击后开始进行模拟数据采集程序(这里使用随机数代替)。2) 关于:Label是dialog,单击后弹出对话框以说明这个程序的版权、帮助等信息。3)
6、 停止:Label是stop,单击后停止程序的运行。4) Chart:用于显示获取的随机数。这是一个非常简单的应用,但是具有一定的代表性。根据要求,该应用至少包含以下5种状态结构。1) Initial:初始化状态;2) Idle:空闲状态,用于响应各种用户界面操作;3) &
7、#160; acquire:采集状态,用于持续模拟采集数据;4) about:用于弹出关于和帮助对话框;5) stop:停止状态,退出循环并中止程序。(a) 背面板(b) 前面板图 1 基本的状态机结构背面板仔细分析图中的基本状态
8、机,可以看出状态始终贯穿整个应用程序,并由移位寄存器进行值的寄存和传递。当前状态分支的结果将决定下一个状态,如图中的Idle状态。在这个状态中,程序将自动检测前面板的三个按钮是否被按下。如果start被按下,则进入acquire状态;如果dialog被按下,则进入about状态;如果stop被按下,则进入stop状态;否则如果没有任何按钮被按下,则仍然进入当前的Idle状态继续检测。在acquire状态中,为了保证程序的重复采集使得下一个状态仍然为acquire,但是这样会导致程序无法停止(中断采集)。于是需要在acquire状态分支中加入stop的探测,如果stop被按下,则不再进入acqu
9、ire状态而直接进入stop状态。从应用1可以看出,基本状态机模式大体上能够满足主程序结构的需要。该模式能够很好地使得应用程序的各个功能以状态的方式有顺序地执行,并且保证了程序的可读性(以状态图的方式显示清晰明了)和扩展性(日后只需要扩展状态即可扩展相应的功能)。事例中使用的是“string”型结构来标记状态,事实上也可以使用其它的数据类型替换,如ring、numeric或enum。从严格意义而言,ring并不属于一种数据类型,它只是一种特殊的numeric性,其性质与numeric基本上一样。尽管ring与enum控件从外观上看是一样的,如图 2所示,但是它们实质上是不同的。主要体
10、现为以下7点:针对基本状态机模式的第(13)个问题,需要对模式进行改进。本节将一一分析这些问题对应的解决方案,并最终形成一种新的状态机模式消息队列型状态机模式。(1) 状态的分类不清晰。这是一个涉及各个状态分类管理的问题,是一个组织问题。我们可以做一个类比,在一个书桌上有许多种类的书籍(通信、计算机、机械、法律等),这些书都摆放在书桌上很整齐。但是我们在寻找一本书时并不会觉得很迅速和随意,因为书籍的摆放是无序的,每次寻找书籍我们不得不从第一本开始浏览直至找到我们想要的书籍。或许可以做一些改变,我们设置一些书立,将不同种类的书使用书立分开
11、。并且在书立上标明这些书籍表示的种类。这样我们在寻找某一种书籍时就不需要从第一本书开始寻找了,只需要找到对应的书立,在这些书立中寻找即可。让我们回到程序,并给程序的状态设置一些“书立”。如图 4所示,系统共有9个有效状态(UI Initial、Data Initial、Instr Initial、Temperature、Power、FFT、JTFA、Data Clean、Exit)。如果把这些状态混在一起,我们需要找到某一个状态时会比较困惑和麻烦。如同上面所述,将这些状态分为4类并设置了4个“书立”(Initial、Acquire、Analyse、System)分隔这些状态。在实际的
12、状态控制中,需要确保程序只会进入实际的状态中运行而不会进入到“书立”分支中,因此对每个“书立”加入了“-”以示区别。图 4 状态分类尽管我们只是进行了少量的修改,但是这的确有利于程序状态的组织和阅读,尤其是当程序具有很多个状态的时候。(2) 缺乏数据共享和错误处理机制。在层叠式的顺序结构中,数据在帧之间的传递是靠“顺序局域变量”实现的。那么如果在case结构中如何传递不同分支的数据呢?这个问题似乎很容易解决,使用局域变量,全局变量或共享变量都能够解决,但是这些并不是最优的解决方案。因为上述的方式会明显系统运行的内存空
13、间和时间。由于状态机的基本组成元素除了case结构之外还有循环,因此可以使用移位寄存器来传递数据。如图 5所示。图 5 状态机中的数据传递图 5使用移位寄存器进行数据共享和传递,将所有的数据封装在一个簇中并对每个数据命名,这样在使用数据时就可以使用“Unbundle by name”或“bundle by name”。需要说明的是,即使使用一个数据需要共享,仍然希望采用簇的封装形式,这样当后续需要增加扩展数据的时候并不会影响现有的数据引用。(3) 每一个状态分支只能够决定后面的一个状态,而无法决定
14、一个状态序列(多个状态)。在基本状态机中之所以存在这个问题是因为状态的传递使用的是Scalar(标量)形式,如果需要传递一个状态序列,很明显可以使用队列或数组进行状态的传递。在LabVIEW程序设计模式中将这种具备处理状态序列的状态机称为“消息队列型状态机”,它是在基本状态机基础上的改进。顾名思义,这种模式就像银行办理业务时排队一样采用队列的方式。当储户进入银行时,首先到叫号机处领取号码进行排队(进入队列)并等待。然后,当前面的储户办理完业务后就可以到相应的窗口办理业务(退出队列)。事实上,这种方式在现代生活中随处可见。在LabVIEW中至少有两种实现消息队列的方法。如图 6所示。前
15、者使用数组函数实现队列元素的入列和出列;后者使用队列函数实现队列元素的入列和出列。二者都能够实现队列的有序操作和状态的序列变化。图 6 消息队列型状态机模式本节解决了基本状态机模式中的(1)(3)个问题,为了更好地比较和使用这些特点,特使用一个实例说明消息队列型状态机的使用过程。【应用2】本例要模拟一个自动贩卖机的工作过程。它的一次正常交易过程为:投币选择需要购买的商品找币,当币值不足或商品已经销售完毕时则无法购买。程序的前面板如图 7所示。在贩卖机的左上侧有4个按钮。(1) 1USD:单击时表示投入1美元
16、的货币,2USD和5USD类同;(2) Change Back:表示找零,也就是将目前剩余的货币退还给用户。程序的右侧是5个按钮,表示5种不同类别的可乐(这里均使用了可口可乐的图标),每种可乐的价格均是1美元。可乐的下面数字表示贩卖机中剩余的该商品的数量,初始为每种20瓶。Current money显示贩卖机中剩余的货币数,你可以继续购买可乐或者选择退回。单击Stop按钮将退出应用程序。本例将使用本节介绍的消息队列状态机模式解决这个应用(也可以使用其它的设计模式)。系统的功能并不复杂,关键是要判断贩卖机中的剩余钱数和剩余的货物数以决定交
17、易是否成功。图 7 自动贩卖机前面板程序背面板如图 8所示。系统分为5个状态,并分为2大类。(1) 第一类:Initiala) UI Initial:前面板界面的初始化。b) Data Initial:数据的初始化。(2) 第二类:Systema)
18、60; Idle(Default):空闲状态。b) CheckMoney:贩卖机中的剩余钱数和剩余的货物数以决定交易是否成功。c) Exit:退出程序。程序开始运行时进入UI Initial和Data Initial状态,完成初始化操作。从图中可以看出系统采用数组函数处理消息队列。图 8 自动贩卖机背面板在UI Init
19、ial中,系统给标题栏和说明栏赋值,并将前面板的商品设置为不可购买状态,因为在初始化时还没有完成投币动作。如图 9所示。图 9 UI Initial分支在Data Initial中包含两个共享的数据:Money和GState,前者表示贩卖机中剩余的币值,初始化值为0;而后者表示贩卖机中各个商品剩余的数量,初始化值为20。数据使用移位寄存器传递以便于在各个case分支中共享和使用,如图 10所示。图 10 Data Initial分支CheckMoney分支主要是为了防止不合法的交易(如投入的币值不足或商品数量不足),如图 1
20、1所示。图 11 CheckMoney分支当程序运行到Exit分支时,将停止循环并退出程序,如图 12所示。图 12 Exit分支Idle分支用来监控前面板各个按钮控件的变化并执行相应的状态。该分支比较复杂,当检测到第0个按钮被按下时(即1USD按钮),贩卖机中的货币值应该加一,同时需要判断是否达到了交易条件(即进入CheckMoney状态)。其它的状态可以执行相应的代码即可,这里不再重复解释。图 13 Idle分支从本例可以看出,相比基本状态机而言,尽管程序的复杂度增加了,但是在构建大型的应用程序时也更加地健壮,代码也易于
21、维护和查看。针对基本状态机模式的第(45)个问题,需要对模式进行改进。本节将一一分析这些问题对应的解决方案,并最终形成一种新的状态机模式用户界面事件模式。(1) 程序一直在占用CPU资源。(2) 无法响应更多的前面板事件。熟悉LabVIEW的工程师应该能够很容易地解决这两个问题,在LabVIEW 7.0以后的版本中提供的事件结构(Event Structure)能够让我们非常便捷地处理这两类问题。在LabVIEW中事件结构的使用并不是一件难事,根据事件的发出源,事件可以抽象地分为
22、用户界面事件和用户自定义事件。相关的基本知识可以参考有关的书籍,这里不再阐述事件结构的使用方法。图 14所示的结构称为用户界面事件模式,它能够很便捷地响应各种事件并且不占用CPU的资源,这是由LabVIEW中事件结构本身的特性决定的。图 14 用户界面事件模式【应用3】本例要模拟一个简单的画图板功能。它有4个功能选项:点(point)、线(line)、圆(circle)和椭圆(oval),一次完成的绘画过程是:在画布上单击鼠标开始绘制按住鼠标的同时在画布上拖动鼠标在画布上放开鼠标结束绘制。程序的前面板如图 15所示,由上下两大部分组成。上面用于选择需要画
23、图的样式,下面是画布,右上方的X表示程序的结束。图 15 画图板前面板由于系统需要响应鼠标在画布上单击、移动和释放事件,因此使用状态机模式是无法解决的,只能通过事件结构。因此本例将使用用户界面事件模式实现上述的画图板功能。程序的背面板如图 16所示。共有4个事件。(1) Panel Close?:响应前面板的X动作,这是一个过滤性事件,当事件发生时并不真正关闭前面板而只是停止程序的运行。(2) Picture <Mouse Down>:表
24、示绘画的开始。(3) Picture <Mouse Move>:表示绘画的路径和轨迹。(4) Picture <Mouse Up> <Mouse Leave>:表示绘画的结束,此时一定要加入<Mouse Leave>事件,因为当鼠标移动到画布的外面时就可以认为是绘画结束了,并不需要一定要求鼠标在画布中释放。图 16 画图板背面板Picture <Mouse Down>事件如图 17所示,这个
25、步骤表示绘制的开始,每次的绘制都必须从这个步骤开始。事件分支左侧的Button参数表示单击鼠标的键位,只有在单击鼠标左键时才被认为是合理的和有效的,当单击其它的键位时并不开始绘制。在有效绘制中,需要将画笔移动到鼠标当前单击的位置。当选择的画图模式是Line、和Point时,使用Draw point.vi函数可以在当前的位置上画一个点并且将画笔移动到当前位置。从图中可以看出系统定义了4个移位寄存器变量以实现不同事件分支的共享,它们的含义如下:(1) 表示当前画布中的图像,事实上就是前面板picture中的内容。因为每次画图时都是在当前画布
26、上图像进行叠加,所以需要使用移位寄存器以避免过多地局域变量。(2) 表示开始绘制时的鼠标位置,也就是Mouse Down在画布上的相对位置,绘制的起点。(3) 表示是否开始了绘制。前面提过每次的绘制过程都是从Mouse Down开始的,如果没有这个动作,那么在鼠标在画布上的移动是无效的。(4) 表示开始绘制时的图像,这个变量与(1)是不一样的。它表示在Mouse Down时画布上的图像,而不是画布中的实时图像。图
27、;17 Picture <Mouse Down>事件Picture <Mouse Move>事件如图 18所示,该事件是绘图的过程中,因此移位寄存器(3)的值必须是true。可以根据不同的画图类型使用相应的函数进行绘图,如画Line时,只需要把当前鼠标的位置作为Line的终点。图 18 Picture <Mouse Move>事件Picture <Mouse Up> <Mouse Leave>事件如图 19所示,该事件表示绘制的结束,因此只需要把移位寄存器(3)的值设置为false即可
28、。图 19 Picture <Mouse Up> <Mouse Leave>事件本例的实现过程并不复杂,但是涉及到了4个移位寄存器变量。一旦变量共享的数据较多时往往需要使用大量的移位寄存器,因为建议使用cluster的形式将各个变量有序地组织起来上两节分别解决了基本状态机的第(15)个问题,但是是否具备一种模式能够综合队列型状态机模式和用户界面事件型模式的优点呢?这样可以同时避免基本状态机的第(15)个问题。答案是肯定的,本节将介绍如何将状态机与事件结构结合起来形成一种新的、稳定的模式。状态机模式的基本构成元素是while循环和case结构,而事件结
29、构模式的基本构成元素是while循环和event结构,因此新的模式应该由while循环、case结构和event结构组成。而while循环的目的是为了保证程序的持续运行,因此必须在最外层,这样就只剩下了图 20所示的两种组合方式。在第一种方式中,每次循环的运行需要经过一个事件结构才能够实现case中各个分支的运行,那么到底需要多少个分支呢?一般而言不同的事件都会有不同的事件处理函数(这些函数可以在case结构中共用),显示这是无法满足要求的,它从本质上而言仍然是一种事件结构。在第二种方式中,程序的主体是一个状态机结构,不同的是在某一个状态分子中有一个事件结构。我们可以回忆状态机模式中
30、的“空闲Idle”状态,这正是长时间占用CPU资源的源头,如果在Idle中加入一个事件结构后就有效地规避了这个问题。图 20 三种结构的组合方式因此图 20中的第二种结构综合了状态机和事件结构的优点,有效地克服了基本状态机的第(15)个问题。此外,在【应用2_自动贩卖机】例程中,按钮1USD、2USD和5USD的作用是相同的,唯一不同的是它们的代表的币值不同。如果我们希望系统共用“币值相加”这个功能,即当这三个按钮任何一个被按下后都调用同一个函数(该函数的功能是将系统中原来的货币值与新加入的币值相加得到新的值)。这样,需要有一种途径把1USD、2USD和5USD代
31、表的币值作为参数传递给函数。图 21所示为带参数的状态机结构,在消息队列的状态机模式中,加入了一个变体型的变量作为状态传递的参数。实际上,可以把红色的部分做成子vi,不仅节省了背面板空间而且能够进行错误处理。程序中应该设置一个专门的错误处理状态,当任何一个状态运行后如果发生错误将直接转到错误处理状态。当然,也可以在图 21的基础上做一些改进和变形,假定参数的数据类型为string型,这样就把Data参数和State合并起来,中间使用特殊符号(如)隔开。图 21 带参数的状态机结构【应用4】本节的例程将使用Multicolumn Listbox控件处理2维
32、数组排序问题,前面板如图 22所示。该Listbox用于显示系统中的各种采集数据值,分为5列。程序的功能是当单击Listbox的列头时,对数据以该列的升序/降序进行排序。单击Stop按钮或×按钮则停止程序运行。图 22 2D数组排序_前面板系统使用状态机和事件结构相结合的模式,如图 23所示。程序分为8个状态,共有4类。各个状态的功能与消息队列型的状态机模式类似,程序加入了错误处理部分。在背面板的循环中共享同一个“错误簇”结构的移位寄存器,当存在错误时程序将暂时停止运行其它的状态而优先进入错误状态(这里是Error分支)。图 23
33、160;2D数组排序_背面板在Idle状态中,事件结构可以防止CPU资源的长时间占用,也可以响应各种前面板事件,如图 24所示。图 24 状态机中的事件结构本例中引入了4个变量以供不同的状态分支调用,如图 25所示。其中的意义如下所述。(1) Index:当前排序的列号,表示Listbox以哪一列为依据进行排序。(2) ASC:表示当前排序的方式,true表示升序,而false表示降序。(3)
34、 Column Header:表示Listbox的列头数据。(4) Data:表示Listbox的内容数据。图 25 数据变量在图 26中,事件结构处理的是Listbox的<Mouse Up>事件,此时只需要对内部的变量赋值即可,并且当该单击是有效单击时进入“DSort”状态进行排序操作。图 26 排序事件在图 27所示的DSort状态中,根据内部变量的值对Listbox赋值并更新列头的显示。图 27 DSort状态图 27调
35、用了2D数组的排序函数,该函数的实现过程如图 28所示。LabVIEW并没有提供2D数组的排序方式,只提供了1D数组的排序函数。本例充分利用了LabVIEW提供的排序函数功能,当然并不是唯一的,也可以使用LabVIEW实现常用的排序算法。图 28 2D数组排序简单而言,动态调用指的是通过程序控制另外一个程序的运行、停止、赋值和获取值等。LabVIEW提供了多种动态调用的方式,从底层而言是通过VI Server技术实现的。图 31所示为LabVIEW中的Application Control选板,动态调用所使用的节点都位于这个选板。当调用一个在硬盘、内存甚
36、至是网络路径上的vi时,首先要使用Open VI Reference以将该VI载入内存并获取VI的“句柄(Reference)”;然后再使用该句柄进行其它的控制操作;最后再关闭该VI的句柄避免内存泄漏,这就完成了一次对VI的调用。图 31 Application Control选板图 32是一个动态调用的具体实现代码,首先使用Open VI Reference获取被动态调用VI的Reference(例子中是C:average.vi);再使用Call By Reference Node节电动态运行该VI;最后关闭VI的Reference。在使用Call By Ref
37、erence Node时需要事先指定被调用VI的输入输出接口,也就是说这种动态调用的前提是必须知道被调用VI的输入输出接口,否则无法进行动态调用。图 32 VI的动态调用Open VI Reference的路径输入是一个多态的输入口,也可以使用String输入,如图 33所示。此时被调用的VI必须在内存中,且输入的是被调用VI的文件名。值得一提的是这种“文件名”调用方式在可执行程序中是无法被调用的,因此建议最好采用路径的调用方式。图 33 Open VI Reference的多态性【应用5】本例将使用LabVIEW的动态调用方式实现斐波那契数列
38、(Fibonacci数列)。斐波那契数列指的是这样一个数列:1,1,2,3,5,8,13,21 这个数列从第三项开始,每一项都等于前两项之和。在数学上表述为:f(n)=f(n-1)+f(n-2),其中n>=3,f(1)=f(2)=1。显然这是一个比较熟悉的递归调用,但是在LabVIEW中似乎很难实现。由于LabVIEW不允许同名的VI同时在内存中,因此一个VI是无法VI调用本身的。但是,通过VI的可重入技术和动态调用技术却可以实现VI的递归调用。图 34所示为Fibonacci数列在LabVIEW中递归的实现方式。case结构有两个分支,当n<=2时直接输出f(
39、n)=1;当n>=3时,输出f(n)=f(n-1)+f(n-2)。此时需要把VI设置为可重入状态。图 34 Fibonacci数列同理我们也可以使用这种递归的方式实现f(n)=n!的算法,从数学上可以写作f(n)=n*f(n-1),其中n>=1,f(0)=1。具体的实例将不再详述。此外,递归算法的效率比较低,在实际应用中应谨慎使用。打开Highlight工具,在Call By Reference Node运行时,程序是处于等待状态的,只有被调用的VI运行完毕,主程序才会继续执行。这似乎无法解决在本节开头提到的问题,那么是否存在一种动态调用方式使被调用的VI与主V
40、I之间分别独立运行呢?答案是肯定的。VI本身是有很多的属性和方法的,如图 35所示。使用这些方法就可以动态控制VI的运行、停止和赋值,各个属性节点和方法的具体含义见LabVIEW的帮助文档。使用这种方式动态调用VI时,并不需要知道VI的输入输出接口。图 35 VI Method图 36是该使用“属性节点和方法”实现动态调用的一个实例。在大多数应用程序启动时会显示一个启动画面用来显示版权、开发单位、软件版本等信息,等待2秒之后关闭启动界面并启动应用程序主界面。图中使用了动态调用的方式启动主程序(Main.vi)并使主程序独立运行,首先运行程序后设置2秒钟的延时;其次,将启动画面的界面设置为“隐藏”(并没有退出内存,只是隐藏了前面板),并且使用Open VI Reference获取VI的句柄;然后使用FP.Open属性打开主程序的前面板(只是打开
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- (2026年)在职教师有偿补课专项治理活动工作总结范文
- 2025年河曲县数学四年级第二学期期末综合测试模拟试题含答案
- 2025年河南省偃师市数学四年级第二学期期中复习检测试题(含解析)
- 消化道出血的急救与护理
- 2025年河北省沧州市盐山县数学四年级下学期期中复习检测试题(含解析)
- 年产3714吨医药原料药及650吨中间体技改项目可行性研究报告模板-立项备案
- DB43-T 3544-2026煤样自动分样封装技术规程
- 职业发展咨询公司副总经理述职报告
- 证券公司突发事件报告管理细则
- 油库安装施工安全措施培训
- 2020风电场设备事故现场处置方案
- 自考《兽医法规14239》考试复习题库(必备版)
- 《中华民族大团结》(初中)第1课-爱我中华教学课件
- 2021废(污)水处理用复合碳源
- 化学(基础模块)中职PPT完整全套教学课件
- 架空导地线-液压压接(郭学闻)
- 技师培训交直流调速
- 输变电工程项目安全管理台账
- 《高级计量经济学》-厦门大学经济学院
- GB/T 9239.1-2006机械振动恒态(刚性)转子平衡品质要求第1部分:规范与平衡允差的检验
- 《绿色建筑概论》整套教学课件
评论
0/150
提交评论