python使用Snap7读写西门子S7系列PLC

1.简介

Snap7

  • Snap7是一个基于s7通信协议的开源软件包,作者是Davide Nardella,该软件包封装了S7通信的底层协议,可使用普通电脑通过编程与西门子S7系列PLC进行通信

  • Snap7三大对象组件:客户端,服务器,合作者。下面是三者关系,更详细介绍可看官网。本篇主要讲述的是Client模式,我们的pc机作为客户端,plc作为服务器。

  • Snap7官网地址:http://snap7.sourceforge.net/

  • Snap7包支持西门子S7-200 SMART,S7-300/400系列,S7-1200/1500系列、另外LOGO! 0BA7/0BA8 PLC、 SINAMICS驱动器也有较好的支持

  • Snap7特点:

    • 基于以太网,网线连接

    • 跨平台,支持 Windows、 Linux、Mac等主流操作系统

      1. Windows系统包括目前主流的Win7/8/10 的32位或64位

      2. Linux系统包括: CentOs、 Debian、 RedHat、 Ubuntu等32位或64位系统

    • 提供多种语言的封装包:C#、VB、C/C++、 Python、java、 Delphi、 LabView等主流编程语言

    • 支持树莓派、 ARDUINO等嵌入式平台

  • python包源码地址:https://github.com/gijzelaerr/python-snap7

2.环境安装

Window

  1. pip install python-snap7(版本一直未更新)
  2. github源码包下载:https://github.com/gijzelaerr/python-snap7(这里会一直更新)
  3. 把snap7.dll和snap7.lib(注意区分32位和64位),设置到环境变量能找到的地址就行

Linux

  • 第一种方式

    1. sudo apt-get install python-pip3
    2. sudo pip3 install python-snap7
  • 第二种方式

    1. 通过以下命令下载snap7:

      git clone https://github.com/lizengjie/snap7-debian.git

    2. 编译:(arm_v7_linux不行就arm_v6_linux)

      cd snap7-debian/build/unix && sudo make -f arm_v7_linux.mk all

    3. 拷贝:

      sudo cp …/bin/arm_v7-linux/libsnap7.so /usr/lib/libsnap7.so
      sudo cp …/bin/arm_v7-linux/libsnap7.so /usr/local/lib/libsnap7.so

    4. sudo ldconfig

不同环境的拷贝文件我稍后放资源管理处

3.连接西门子plc

方法介绍

​ 在Client类里提供了主要5种设置连接plc函数

  • connect(ip, rack, slot)

    这是连接plc的唯一方法,参数ip是要连接的plcip地址,rack是机架号,slot卡槽号,不同的plc对应不同的机架和卡槽看下图对应

    plc rack slot
    s7-200smart 0 1
    s7-300 0 2
    s7-400/WIN AC 见硬件组态 见硬件组态
    s7-1200/1500 0 0/1
  • set_connection_params(ip, local_tsap, remote_tsap)

    这是设置远程本地TSAP和远程TSAP的函数,三个参数ip是plcIP地址,本地TSAP和远程TSAP是相对应的int类型,函数一般用于连接logo系列时使用,调用顺序在connect()之前调用

  • set_connection_type(connection_type)

这是用来设置连接属性的函数,connection_type参数范围如图:

connection_type
PG 1
OP 2
S7-Basic 3-10

此函数不是一定要调用,如果调用一定要在connect()函数之前调用设置,比如连接s7-200SMART就一定要调用此函数,参数一般设为3

  • disconnect() 和 destroy()

    这是断开客户端连接和销毁客户端连接,无需参数

  • 最后注意如果你set_connection_params()和set_connection_type()都要调用,一定要set_connection_type()在前set_connection_params()在后,因为顺序相反会对远程TASP造成影响。所以整体的顺序应该是

    set_connection_type(connection_type) #选用
    set_connection_params(ip, local_tsap, remote_tsap)  #选用
    connect(ip, rack, slot)
    disconnect()
    destroy()
    

连接plc

  1. 第一步首先实例化一个Client对象

    from snap7 import clientmy_plc = client.Client()
    
  2. 第二步调用connect()的方法

    from snap7 import clientmy_plc = client.Client()# my_plc.set_connection_type(3)  如果连接的是s7-200smart系列plc
    # set_connection_params(ip, local_tsap, remote_tsap)  如果连接的是logo!系列plcmy_plc.connect(ip, rack, slot)
    # ip是plcIP,rack是机架号,slot卡槽号,不同的plc对应不同的机架和卡槽看上边表格print(my_plc.get_connected())
    # 判断连接成功可调用get_connected():返回True就是成功,不成功直接报错。
    
  3. 第三步断开连接

    from snap7 import client
    my_plc = client.Client()
    my_plc.connect(ip, rack, slot)
    my_plc.disconnect()
    my_plc.destroy() # 不用了一定要断开销毁客户端
    
  • 代码示例

    from snap7 import clientdef connect_logo(ip: str, local_tsap: int, remote_tsap: int, rack: int, slot: int):"""连接logo系列:param ip: PLC/设备IPV4地址:param local_tsap: 本地tsap(PC tsap):param remote_tsap: 远程tsap(PLC tsap):param rack: 服务器上的机架:param slot: 服务器上的插槽"""# 初始化一个客户端my_plc = client.Client()# 设置内部(IP、LocalTSAP、RemoteTSAP)坐标。必须在connect()之前调用此函数my_plc.set_connection_params(ip, local_tsap, remote_tsap)# 连接到S7服务器my_plc.connect(ip, rack, slot)return my_plcdef connect_200smart(ip: str, plc_model=3, rack=0, slot=1):"""连接s7-200smart系列:param ip: PLC/设备IPV4地址:param plc_model: 连接类型:1用于PG,2用于OP,3至10用于S7基本:param rack: 服务器上的机架:param slot: 服务器上的插槽"""# 初始化一个客户端my_plc = client.Client()# 设置连接资源类型,即客户端,连接到PLCmy_plc.set_connection_type(plc_model)# 连接到S7服务器my_plc.connect(ip, rack, slot)return my_plcdef connect_plc(ip: str, rack: int, slot: int):"""连接s7-1200/1500系列:param ip: PLC/设备IPV4地址:param rack: 服务器上的机架:param slot: 服务器上的插槽"""my_plc = client.Client()my_plc.connect(ip, rack, slot)return my_plc
    

4.读plc

方法介绍及示例

在Client类里提供了主要两种读plc的函数

  • read_area(area, dbnumber, start, size)

    这是读plc最最最重要的方法,功能强大,支持(I,Q,M,DB,V,CT,TM)多存储区读取数据

    ​ area:区地址类型(十六进制类型),如下图对应

    ​ dbnumber:地址编号(int),只适用于DB区和200samart的V区,其它区全默认0,V区只能填1

    ​ start:要读取数据的字节起始地址(int)

    ​ size:要读取的数据类型所占字节长度大小(int),如下字典对应

    I Q M DB/V CT TM
    0x81 0x82 0x83 0x84 0x1C 0x1D
    # 不同类型所占字节大小
    TypeSize = {'int': 2,  # 有符号(-32768~32767)'bool': 1,  # bool值'dint': 4,  # 有符号 (-2147483648~2147483647)'word': 2,  # 无符号(0~65536)'real': 4,  # 有符号 float类型(这范围记不住了)'dword': 4,  # 无符号(0~4294967295)'char': 1,  # CHAR,ASCII字符集,占用1个字节内存,主要针对欧美国家(字符比较少)'string': 255,  # STRING,占用256个字节内存,ASCII字符串,由ASCII字符组成's5time': 2,'wchar': 2,  # WCHAR,Unicode字符集,占用2个字节内存,主要针对亚洲国家(字符比较多)'wstring': 512,  # WSTRING,默认占用512个字节内存(可变),Unicode字符串,由Unicode字符构成'dt': 4,  # DateTime 日期'usint': 1,  # 0~255'sint': 1,  # -128~127'uint': 2,  # 0~4294967295'udint': 4,  # 0~4294967295'lreal': 8,'time': 4,'d': 2,'tod': 4,  # TOD (TIME_OF_DAY)数据作为无符号双整数值存储,被解释为自指定日期的凌晨算起的毫秒数(凌晨 = 0ms)。必须指定小时(24 小时/天)、分钟和秒。可以选择指定小数秒格式。'dtl': 12,  # DTL(日期和时间长型)数据类型使用 12 个字节的结构保存日期和时间信息。可以在块的临时存储器或者 DB 中定义 DTL 数据。'date': 2,  # Date(16位日期值)、'ltod': 8
    }

    return:函数最后返回的是一个字节数组,到这里大家不用自己用struct包去解,作者在uitl文件里为大家封装了取不同类型变量值的函数,下面我主要介绍两种,

    • bool:get_bool(_bytearray, byte_index, bool_index)

      ​ _bytearray:字节数组,就是你上面读到的字节数组

      ​ byte_index:字节索引,这里填0就可以,后面我会详细介绍byte_index和上面read_area()的参数start,size三者的关系,以及灵活应用

      ​ bool_index: bool值索引,其实就是位(bit)索引(0~7),因为1byte=8bit

    • real:get_real(_bytearray, byte_index)

      ​ 参数同上,大家可自己看源码,目前除了bool和string类型,其它都只要两个参数_bytearray和bool_index,有一些类型作者还没写,大家有用到可以自己解。

    """简单示例#1plc:    s7-1200变量地址:DB1.DBD36 (1是地址编号,36是起始值)类型:   real(float)
    """
    from snap7 import util, client
    from snap7.snap7types import S7AreaDBmy_plc = client.Client()  # 实例化客户端
    my_plc.connect('192.168.2.1', 0, 0)  # 连接s7-1200
    byte_arrays = my_plc.read_area(S7AreaDB, 1, 36, 4)  # 读出变量的字节数组
    value = util.get_real(byte_arrays, 0)  # 通过数据类型取值
    my_plc.disconnect()  # 断开连接
    my_plc.destroy()  # 销毁
    print(value)--------------------------------------------------------------------------------------"""简单示例#2plc:    s7-200SMART变量地址:M1.0 (1是起始值,0是bool索引)类型:   bool
    """
    from snap7 import util, client
    from snap7.snap7types import S7AreaMKmy_plc = client.Client()  # 实例化客户端
    my_plc.set_connection_type(3)  # 设置连接资源类型
    my_plc.connect('192.168.2.2', 0, 1)  # 连接s7-200SMART
    byte_arrays = my_plc.read_area(S7AreaMK, 0, 1, 1)  # 读出变量的字节数组
    value = util.get_bool(byte_arrays, 0, 0)  # 通过数据类型取值
    my_plc.disconnect()  # 断开连接
    my_plc.destroy()  # 销毁
    print(value)
    

这里介绍一下start,size和byte_index之间关系以及如何应用,方便理解个人整理以下如图

注意多个变量的适用条件必须为同一地址(area),同一地址编号(dbnumber)。所以通过read_area函数可以一次读取同一地址编号上的所有变量

"""示例plc:    s7-1200变量地址:[DB4.DBX0.1, DB4.DBD36, DB4.DBW2 .....]类型:   [bool, float, word ......]
"""
from snap7 import util, client
from snap7.snap7types import S7AreaDBmy_plc = client.Client()
my_plc.connect('192.168.2.1', 0, 0)  byte_arrays = my_plc.read_area(S7AreaDB, 4, 0, 40)
# 这是所有db块,地址编号4的变量,套用图上公公式,最小的起始值是0,size是最大起始值加它类型所占的字节数就是36+float类型所占4个byte长度,所以size是40value1 = util.get_bool(byte_arrays, 0, 1)
# DB4.DBX0.1是bool类型,byte_index = 起始值是0 - 最小的起始值0 = 0value2 = util.get_real(byte_arrays, 36)
# DB4.DBD36是float类型,byte_index = 起始值是36 - 最小的起始值0 = 36value3 = util.get_word(byte_arrays, 2)
# DB4.DBW2是word类型,byte_index = 起始值是2 - 最小的起始值0 = 2my_plc.disconnect()
my_plc.destroy()
print(value1, value2, value3)
  • read_multi_vars(items)

    这是可以一次读取<=19个不同地址类型的变量,由于pdu大小限制一次性读取不能超过19个变量

    items参数是一个由S7DataItem实例对象组成的列表

    下面我用作者写的一个示例给大家介绍一下

    import ctypes
    import snap7
    from snap7.common import check_error
    from snap7.types import S7DataItem, S7AreaDB, S7WLByteclient = snap7.client.Client()
    client.connect('10.100.5.2', 0, 2)data_items = (S7DataItem * 3)()  # 注意就是这里数字不能大于19data_items[0].Area = ctypes.c_int32(S7AreaDB)  # 地址类型
    data_items[0].WordLen = ctypes.c_int32(S7WLByte)  # 这里的WordLen除了读TM和CT地址时其它地址统一用字节(S7WLByte)。不要用S7WLBit,用位去读需要换算,不嫌麻烦你可以试试
    data_items[0].Result = ctypes.c_int32(0)  # result用不到写0就可以
    data_items[0].DBNumber = ctypes.c_int32(200)  # 地址编号
    data_items[0].Start = ctypes.c_int32(16)  # 变量起始字节地址
    data_items[0].Amount = ctypes.c_int32(4)  # 字节长度data_items[1].Area = ctypes.c_int32(S7AreaDB)
    data_items[1].WordLen = ctypes.c_int32(S7WLByte)
    data_items[1].Result = ctypes.c_int32(0)
    data_items[1].DBNumber = ctypes.c_int32(200)
    data_items[1].Start = ctypes.c_int32(12)
    data_items[1].Amount = ctypes.c_int32(4)  # reading a REAL, 4 bytesdata_items[2].Area = ctypes.c_int32(S7AreaDB)
    data_items[2].WordLen = ctypes.c_int32(S7WLByte)
    data_items[2].Result = ctypes.c_int32(0)
    data_items[2].DBNumber = ctypes.c_int32(200)
    data_items[2].Start = ctypes.c_int32(2)
    data_items[2].Amount = ctypes.c_int32(2)  # reading an INT, 2 bytes# create buffers to receive the data
    # use the Amount attribute on each item to size the buffer
    for di in data_items:# create the bufferbuffer = ctypes.create_string_buffer(di.Amount)# cast the pointer to the buffer to the required typepBuffer = ctypes.cast(ctypes.pointer(buffer), ctypes.POINTER(ctypes.c_uint8))di.pData = pBufferfor di in data_items:check_error(di.Result)result, data_items = client.read_multi_vars(data_items)result_values = []
    # function to cast bytes to match data_types[] above
    byte_to_value = [snap7.util.get_real, snap7.util.get_real, snap7.util.get_int]# unpack and test the result of each read
    for i in range(0, len(data_items)):btv = byte_to_value[i]di = data_items[i]value = btv(di.pData, 0)result_values.append(value)
    print(result_values)client.disconnect()
    client.destroy()
    

5.写plc

方法介绍及示例

对变量赋值同样也介绍两种方法

  • write_area(area, dbnumber, start, data)

要想对变量赋值,必须先读取变量数组,然后在把要写入的值设置到缓存,最后在写到plc。

三步顺序:

​ 第一步:byte_arrays = read_area() 或 byte_arrays = bytearray(变量类型所占字节大小)

​ 这个不介绍了不懂看上面读plc方法介绍

​ 第二步:在snap7.util里作者同样封装了不同类型变量更改字节数组的方法,这里拿bool类型描述一下,因为 其他类型参数基本都一样,大家可看源码

​ set_bool(_bytearray, byte_index, bool_index, value)

​ _bytearray:字节数组

​ byte_index:字节索引

​ bool_index:位索引

​ value:要写入的值(注意必须与要赋值的变量类型一致)

​ 第三步:在通过write_area()函数把值写进plc

​ write_area(area, dbnumber, start, data)

​ area:地址类型

​ dbnumber: 地址编号

​ start:字节起始值

​ data: 字节数组(就是你第一步读出来的字节数组)

"""简单示例#1plc:    s7-200SMART变量地址:M1.0 (1是起始值,0是bool索引)类型:   bool
"""
from snap7 import util, client
from snap7.snap7types import S7AreaMKmy_plc = client.Client()
my_plc.set_connection_type(3)
my_plc.connect('192.168.2.101', 0, 1)
byte_arrays = my_plc.read_area(S7AreaMK, 0, 1, 1)
print('赋值前', util.get_bool(byte_arrays, 0, 0))
util.set_bool(byte_arrays, 0, 0, 1)
my_plc.write_area(S7AreaMK, 0, 1, byte_arrays)
print('赋值后', util.get_bool(byte_arrays, 0, 0))
my_plc.disconnect()
my_plc.destroy()-----------------------------------------------------------------------------------------"""简单示例#2plc:    s7-1200变量地址:Q1.2 (1是起始值,2是bool索引)类型:   bool
"""
from snap7 import util, client
from snap7.snap7types import S7AreaPAmy_plc = client.Client()
my_plc.connect('192.168.2.1', 0, 1)
byte_arrays = my_plc.read_area(S7AreaPA, 0, 1, 1)
print('赋值前', util.get_bool(byte_arrays, 0, 2))
util.set_bool(byte_arrays, 0, 2, 1)
my_plc.write_area(S7AreaPA, 0, 1, byte_arrays)
print('赋值后', util.get_bool(byte_arrays, 0, 2))
my_plc.disconnect()
my_plc.destroy()

同读的思维一样,我们这里也可以一次为同一地址,同一地址编号所有变量赋值

"""示例plc:    s7-200SMART变量地址:V100.0  VD104类型:   bool    real
"""
from snap7 import util, client
from snap7.snap7types import S7AreaDBmy_plc = client.Client()
my_plc.set_connection_type(3)
my_plc.connect('192.168.2.101', 0, 1)
byte_arrays = my_plc.read_area(S7AreaDB, 1, 0, 108)
print('赋值前', util.get_bool(byte_arrays, 100, 0), '赋值前', util.get_real(byte_arrays, 104))
util.set_bool(byte_arrays, 100, 0, 1)
util.set_real(byte_arrays, 104, 999.99)
my_plc.write_area(S7AreaDB, 1, 0, byte_arrays)
print('赋值后', util.get_bool(byte_arrays, 100, 0), '赋值后', util.get_real(byte_arrays, 104))
my_plc.disconnect()
my_plc.destroy()
  • write_multi_vars(items)

这同样也是一个可以一次为多个不同地址变量赋值的函数(同样不能大于19个)

还是用作者的例子(参数用法大同小异)

import ctypes
import snap7
from snap7.types import S7WLByte, S7DataItem, S7WLWord, S7WLReal, S7WLTimer
from snap7.types import areas, wordlen_to_ctypes
from snap7.util import set_int, set_real, set_word, get_int, get_real, get_s5timeclient = snap7.client.Client()
client.connect('192.168.100.100', 0, 2)items = []def set_data_item(area, word_len, db_number: int, start: int, amount: int, data: bytearray) -> S7DataItem:item = S7DataItem()item.Area = ctypes.c_int32(area)item.WordLen = ctypes.c_int32(word_len)item.DBNumber = ctypes.c_int32(db_number)item.Start = ctypes.c_int32(start)item.Amount = ctypes.c_int32(amount)array_class = ctypes.c_uint8 * len(data)cdata = array_class.from_buffer_copy(data)item.pData = ctypes.cast(cdata, ctypes.POINTER(array_class)).contentsreturn itemint_values = [10, 20, 30, 40]
ints = bytearray(len(int_values) * 2)
for i, value in enumerate(int_values):set_int(ints, i * 2, value)real = bytearray(4)
set_real(real, 0, 42.5)counters = 0x2999.to_bytes(2, 'big') + 0x1111.to_bytes(2, 'big')item1 = set_data_item(area=areas.DB, word_len=S7WLWord, db_number=1, start=0, amount=4, data=ints)
item2 = set_data_item(area=areas.DB, word_len=S7WLReal, db_number=1, start=8, amount=1, data=real)
item3 = set_data_item(area=areas.TM, word_len=S7WLTimer, db_number=0, start=2, amount=2, data=counters)items.append(item1)
items.append(item2)
items.append(item3)client.write_multi_vars(items)db_int = client.db_read(1, 0, 8)
db_real = client.db_read(1, 8, 12)
db_counters = client.ct_read(2, 2)print(f'int values: {[get_int(db_int, i * 2) for i in range(4)]}')
print(f'real value: {get_real(db_real, 0)}')
print(f'counters: {get_s5time(counters, 0)}, {get_s5time(counters, 2)}')

6.总结

不总了,有问题大家随时交流

python使用Snap7读写西门子S7系列PLC相关推荐

  1. python实时读plc数据_python snap7读写西门子S系列PLC寄存器的值(PLC的I、Q、M、DB区)...

    西门子 SiemensTCP/IP调试助手工具是利用python 开发的,适用于西门子S7-300.S7-400.S7-1200.S7-1500等具备Ethernet TCP/IP通讯方式的PLC的( ...

  2. Java读写操作西门子S7系列PLC

    简介 Java实现操作西门子S7系列PLC,基于开源项目s7connect实现,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性 ...

  3. C# 读取西门子S7系列PLC教程及源码

    创建 PLC 实例,连接和断开连接 若要创建驱动程序的实例,需要使用此构造函数: public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot) ...

  4. 西门子S7系列PLC安全防护研究

    近年来,随着中国制造的不断崛起,工业控制系统已成为国家关键基础设施的重中之重,工控系统的安全问题也随之而来.工控产品的多样化,造成了工控系统网络通讯协议不同,大量的工控系统采用私有协议,从而导致协议存 ...

  5. 西门子S7系列PLC如何实现工业互联?(S7中间件)

        为了更加方便快捷地提供西门子S7系列PLC数据交换到工业云平台(WebAPP或移动端APP),作者开发了西门子S7系列支持TCP/IP连接方式的PLC数据交换平台-S7数据采集及交换平台(以下 ...

  6. 西门子逻辑运算指令_西门子S7系列plc逻辑运算指令

    西门子S7系列plc逻辑运算指令: 有关西门子S7系列plc逻辑运算指令,包括字节逻辑运算指令,IN1和IN2还可以是常数,字逻辑运算指令,双字逻辑运算指令. 1.字节逻辑运算指令 ANDBIN1,O ...

  7. 西门子S7系列PLC以太网通讯处理器MPI-131

    基本说明:MPI-131用于西门子 SIMATIC S7 系列 PLC(包括 S7-200. S7-300. S7-400).西门子数控机床(840D,840DSL等)的以太网通讯,支持以太网编程下载 ...

  8. 艾默生DCS(ModbusTCP)与西门子S71500系列PLC(PROFINET)通讯

    艾默生DCS(ModbusTCP)与西门子S71500系列PLC(PROFINET)通讯 艾默生DCS控制系统需要和西门子的PLC控制系统交互数据,德国赫优讯NT100-RE-EN通讯网关提供了快捷可 ...

  9. tinyint对应什么数据类型_学习西门子S7-200系列PLC不得不掌握的数据类型

    在学习PLC的过程中经常会有说到数据类型这个概念,那到底什么是数据类型?数据类型有什么作用?在西门子200系列PLC中的数据类型有哪些?这些都是学习西门子PLC不等不掌握的内容? 那到底什么是数据类型 ...

  10. MatrikonOPC与西门子S7300系列PLC以太网通讯

    摘要 MatrikonOPC通过以太网连接西门子S7300系列PLC,NET30-MPI通讯桥接器为PLC提供以太网通讯接口.通过MatrikonOPC采集现场设备的实时生产和设备数据.主要设备的控制 ...

最新文章

  1. 服务器论坛有哪些_SEO工作中,经常使用的无效外链有哪些?
  2. 【技术杂谈】RPC和RESTful API入门篇
  3. 【C++ 语言】面向对象 ( 模板编程 | 函数模板 | 类模板 )
  4. 210130阶段三socket服务器
  5. 查看Linux服务器的CPU详细信息
  6. 12 Essential Bootstrap Tools for Web Designers
  7. 信息学奥赛一本通C++语言——1057:简单计算器
  8. MTK PerfService介绍
  9. php可逆加密解密函数,php 好用可逆的 加密解密 函数。
  10. IIS 发布的FTP提供下载时的转码问题
  11. 阿里云高级技术专家彦林:云原生架构下的微服务演进
  12. 如何使用@PostConstruct初始化敏感词库和hutool过滤敏感词信息
  13. 二叉树遍历的一些非递归算法
  14. McAfee迈克菲杀毒软件企业版8.8.13-McAfee VirusScan Enterprise8.8 百度云
  15. html - - - 设置网页图标logo
  16. 沧海的孤塔-chimera
  17. 怎么查看笔记本内存条型号_笔记本内存条型号简介以及查看方法【图文教程】...
  18. 【产品开发】北邮国际学院大二下期末复习
  19. html标签手册 360doc,基于AJAX的文件上传控件NetAdvantage for jQuery
  20. 温度转换代码(摄氏度华氏度转换)

热门文章

  1. git-svn使用教程:git与svn进行同步
  2. 【韦东山嵌入式Linux】vi编辑器入门笔记
  3. 省级面板数据(1990-2019):能源生产等(原油、石油、焦炭、原煤、天然气等)stata或excel版本
  4. fluent二维叶型仿真_FLUENT太阳能热水器仿真
  5. 淘淘商城第1讲——你给介绍介绍,什么叫淘淘商城?
  6. Kali下安装Wiznote
  7. 实对称矩阵的特征值求法_旋转之三 - 旋转矩阵
  8. Allwinner(全志)V3s Camera sensor Support List
  9. 分区魔术师于分区助手怎么选择
  10. admui3字体无法删除_做了一个过往字体汇总,含字体使用教程及简易搜寻字体方法...