wangluo_dx
wangluo_dx
第一次上网98年
关注数: 35
粉丝数: 20
发帖数: 93
关注贴吧数: 3
与西门子通讯 一直想把三菱和西门子这两个使用频率最高的PLC上位通讯,融合到WCS系统的框架里; 现在三菱主流使用Q系列,使用的是MC协议, 前一段时间也写过一个入门介绍: 三菱Q系列通讯方式设计说明 去年8月份,无意中发现用网络抓包工具可以一点不漏的抓取通讯包,简单摸索一下,也把规律摸索的七七八八了, 也写了一个简单的说明: 终于破解了西门子通讯协议 但是真正用于项目,就需要100%的摸索出来; 一直都想早点弄出来,代替OPC, 但是拖延症太厉害,一直拖了半年,都没有进一步去完善; 这两天过年期间,难得心里静下来,不去考虑老项目维护,也暂时把手头上新项目先放下,脑子可以放空,把这个拖延了10几年的问题先处理掉; 这么多年来,做项目一直计较框架的设计; 每次新项目下来,都有些新的个性化的东西, 这时都会就会发现之前的框架里又有些不满足的地方; 这时候就会纠结; 到底是沿用老框架,迅速做项目;还是先把老框架升级好,再做项目; 如果不革新,项目就压的你就没机会升级;小米的MIUI基本每周升级一次;我们这点升级其实算不了什么; 这么大岁数了,能干活也没几年了,在避免不了的被淘汰之前, 还是咬牙升级框架升级好;不要被甩的太远; 想想白居易的, 印象最深的几句:门前冷落鞍马稀,老大嫁作商人妇,商人重利轻别离 ;一直靠拼体力生存,岁月最终会让你体会到门可罗雀的凄凉; 再看看最冯小刚主演的; 也许未曾拥有,就被时代的洪流给淹没了;如今互谅网时代,苟延残喘的混着吧。 言归正传,先说说 1.西门子和三菱的几个区别(上位只关心的通讯层面): 西门子PLC通讯端口固定102,但是可以连接多个PC端(客户端),三菱PLC通讯端口可以自定义,最多好像8个,但是每个端口只能连接一个客户端; 两者的读写指令类似,但是西门子在端口连接的时候,要做两个初始化指令交互后,才能正常读写处理; 如果中途有错误格式的指令,可能导致端口连接断开; 三菱PLC主要是以字为单位读写的;西门子主要是以字节为单位读写; 所以三菱相邻两个地址相差16bit,西门子相邻两个地址相差8bit; 4.三菱PLC的数据块,一般最小处理单位就是字,很少拆成bit处理(或者把整个字当作0,1布尔类型处理,但是这样有点太浪费了), 而且上位PC端只能用字去读写,无法按位读写,如果真的要用bit处理,一般就用M点; 西门子这块比较灵活,可以按bit或byte去读写;如果按byte,标识的样子是:DB10.B99 ;如果是bit,标识的样子是:DB10.X99.0~DB10.X99.7 5.三菱PLC的数据块是固定的,比如D0~D6000; 西门子的数据块是通过西门子的编程工具初始化的,也就是说,你可以把一片地址定义成DB10,也可以定义成DB50; 通俗的说:三菱PLC的数据库偏硬; 西门子的偏软,它的地址是映射的虚拟地址; 三菱的数据位是从小到大的,比如某个双字,低位在前,高位在后;这是针对数字类型,但是如果是ascii码,因为一个字有两个字节,这时候却又是反的; 所以在三菱里面对数字和字符类型,要分两种顺序处理; 西门子是从大到小的;这两种方法有什么区别呢; 简单来说:从小到大主要是计算机思考的方式; 从大到小是人的思考方式; 比如655539,它等于65536+3,转换成16进制是0x00010003 需要两个字 , 如果在三菱里存储的顺序就是先低位3,再高位1,也就是 03 00 01 00; 在西门子里存储的顺序从高到低,也就是00 01 00 03; 就像oracle在的数据在windows系统里的数据存储顺序是从小到大,在liunx系统里又是从大到小; 2.报文的基本格式: 2.1 第1和第2个字节是:固定报文头03 00,这里我们就用到三种报文: a.初始化 b. 读 c.写,都是这种格式; 2.2 第3和第4个字节是:整个报文的长度; 其它部分就是各种报文的个性化处理了; 下面分析大量报文的案例进行规律分析,为了便于对照,每种都用1200 和300 两种对照demo显示: 3.初始化报文 初始化报文分两个交互; 3.1 交互一 西门子1200: PC发出报文 ( [A18]=0x01 =CPUSlot) 03 00 00 16 11 E0 00 00 00 01 00 C1 02 01 00 C2 02 01 01 C0 01 09 PLC回复报文( B[10]=0x06 可能 是西门子的小型号 B[22]=0x01=CPUSlot) 03 00 00 16 11 D0 00 01 00 06 00 C0 01 09 C1 02 01 00 C2 02 01 01 西门子300: PC发出报文 ( A[18]=0x02 =CPUSlot) 03 00 00 16 11 E0 00 00 00 01 00 C1 02 01 00 C2 02 02 01 C0 01 09 PLC回复报文 (B[10]=0x04 可能 是西门子的小型号 B[22]=0x0=CPUSlot) 03 00 00 16 11 D0 00 01 00 04 00 C0 01 09 C1 02 01 00 C2 02 01 02 opc 对 1200 和 300 不用配置的不同点,就一个地方:前者 CPUSlot = 1 ,后者CPUSlot = 2; 所以可以摸索规律是: a.pc发起第一个初始化报文的时候,第18个字节标识了CPUSlot ; b.plc回复报文和读取报文长度一样都是22个字节长度; c.plc回复报文的最后一个字节也是CPUSlot ,这个可以用来校验; d. plc回复的第10个字节一个是06,一个是04,这个好像是小型号的区别; 细节摸索下来:1200该字节是06,314是04,315是03;咱写程序的时候,就不要考虑这个来校验了; 3.2交互二 PC发出报文 03 00 00 19 02 F0 80 32 01 00 00 FF FF 00 08 00 00 F0 00 00 01 00 01 07 80 PLC回复报文 03 00 00 1B 02 F0 80 32 03 00 00 FF FF 00 08 00 00 00 00 F0 00 00 01 00 01 00 F0 第二个初始化报文交互,通过1200 和314,315的对比,发现居然完全没有任何区别; 所以我们可以把这个交互完全固话; 到此,整个初始化处理就算结束了,正常在设计架构的时候,可以这么实现: 在ClentSocket的onConnect(即正常连接上)的瞬间,pc给plc发起第一个初始请求,得到回复后(为了简单,就仅仅判断长度为22即可); 立刻发起第二个固定的初始话请求,得到长度为24的报文后,就通过一个布尔变量通知整个系统可以正常读写; 4.读操作 读demo1: 西门子1200: 读取DB10, count=17 ,offset=19 PC发出报文 (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x001C=序列号,A[24]~A[25]=0x0011=17=读取请求count; A[26]~A[27]=0x000A=10=DB10, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000098=152=198=读取偏移量offset(bit为单位) ) 03 00 00 1F 02 F0 80 32 01 00 00 00 1C 00 0E 00 00 04 01 12 0A 10 02 00 11 00 0A 84 00 00 98 PLC回复报文: (B[3]~B[4]=0x002A=42=回复报文总长度, B[12]~B[13]=0x001C=序列号,B[16]~B[17]=0x0015=21=读取请求count(17)+4 B[24]~B[25]=0x0088=178=请求数据长度(bit为单位), B[26]~最后=数据值) 03 00 00 2A 02 F0 80 32 03 00 00 00 1C 00 02 00 15 00 00 04 01 FF 04 00 88 13 14 15 16 17 00 00 00 00 00 00 00 00 00 00 00 00 读demo2: 西门子1200: 读取DB11, count=17 ,offset=19 PC发出报文: (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x008E=序列号,A[24]~A[25]=0x0011=17=读取请求count; A[26]~A[27]=0x000B=11=DB11, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000098=152=198=读取偏移量offset(bit为单位) ) 03 00 00 1F 02 F0 80 32 01 00 00 00 8E 00 0E 00 00 04 01 12 0A 10 02 00 11 00 0B 84 00 00 98 PLC回复报文: (B[3]~B[4]=0x002A=42=回复报文总长度, B[12]~B[13]=0x001C=序列号,B[16]~B[17]=0x0015=21=读取请求count(17)+4 B[24]~B[25]=0x0088=178=请求数据长度(bit为单位), B[26]~B[42]=数据值) 03 00 00 2A 02 F0 80 32 03 00 00 00 8E 00 02 00 15 00 00 04 01 FF 04 00 88 13 14 15 16 17 18 00 00 00 00 00 00 00 00 21 22 23 读demo3: 西门子1200:读取DB11, count=16 ,offset=18 PC发出报文: (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0013=序列号,A[24]~A[25]=0x0010=16=读取请求count; A[26]~A[27]=0x000B=11=DB11, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000090=146=188=读取偏移量offset(bit为单位) ) 03 00 00 1F 02 F0 80 32 01 00 00 00 13 00 0E 00 00 04 01 12 0A 10 02 00 10 00 0B 84 00 00 90 PLC回复报文: (B[3]~B[4]=0x0029=41=回复报文总长度, B[12]~B[13]=0x0013=序列号,B[16]~B[17]=0x0014=20=读取请求count(16)+4 B[24]~B[25]=0x0080=168=请求数据长度(bit为单位), B[26]~B[41]=数据值) 03 00 00 29 02 F0 80 32 03 00 00 00 13 00 02 00 14 00 00 04 01 FF 04 00 80 00 13 14 15 16 17 18 00 00 00 00 00 00 00 00 21 读demo4: 西门子300 (314) 读取D50, count=20 ,offset=4000 PC发出报文: (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0028=序列号,A[24]~A[25]=0x0014=20=读取请求count; A[26]~A[27]=0x0032=50=DB50, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x007D00=32000 =40008=读取偏移量offset(bit为单位) ) 03 00 00 1F02 F0 80 32 01 00 00 00 28 00 0E 00 00 04 01 12 0A 10 02 00 14 00 32 8400 7D 00 PLC回复报文: (B[3]~B[4]=0x002D=45=回复报文总长度, B[12]~B[13]=0x0028=序列号,B[16]~B[17]=0x0018=24=读取请求count(20)+4 B[24]~B[25]=0x00A0=208=请求数据长度(bit为单位), B[26]~B[45]=数据值) 03 00 00 2D02 F0 80 32 03 00 00 00 28 00 02 00 1800 00 04 01 FF 04 00 A0 00 04 0E AB 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 读demo5: 西门子300 (315) 读取D10, count=100 ,offset=2 PC发出报文: (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0003=序列号,A[24]~A[25]=0x0064=100=读取请求count; A[26]~A[27]=0x000A=10=DB10, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000010=16=28=读取偏移量offset(bit为单位) ) 03 00 00 1F 02 F0 80 32 01 00 00 00 03 00 0E 00 00 04 01 12 0A 10 02 00 64 00 0A 84 00 00 10 PLC回复报文: (B[3]~B[4]=0x007D=125=回复报文总长度, B[12]~B[13]=0x0003=序列号,B[16]~B[17]=0x0068=104=读取请求count(100)+4 B[24]~B[25]=0x0320=1008=请求数据长度(bit为单位), B[26]~B[125]=数据值) 03 00 00 7D 02 F0 80 32 03 00 00 00 03 00 02 00 68 00 00 04 01 FF 04 03 20 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 读demo6: 西门子1200 读取X输入(input)两个byte: PC发出报文: (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0002=序列号,A[24]~A[25]=0x0002=2=读取请求count; A[26]~A[27]=0x000A=10=DB10[其实这里写什么都可以,因为input不属于DB块], A[28]=0x81=读取的数据类型为Input,A[29]~A[31]=0x000000=0=08=读取偏移量offset(bit为单位) ) 03 00 00 1F 02 F0 80 32 01 00 00 00 02 00 0E 00 00 04 01 12 0A 10 02 00 02 00 0A 81 00 00 00 PLC回复报文: (B[3]~B[4]=0x001B=27=回复报文总长度, B[12]~B[13]=0x0002=序列号,B[16]~B[17]=0x0068=104=读取请求count(100)+4 B[24]~B[25]=0x0320=1008=请求数据长度(bit为单位), B[26]~B[27]=数据值) 03 00 00 1B 02 F0 80 32 03 00 00 00 02 00 02 00 06 00 00 04 01 FF 04 00 10 08 00 读demo7: 西门子1200 读取Y输出(output)两个byte: PC发出报文: (A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0001=序列号,A[24]~A[25]=0x0002=2=读取请求count; A[26]~A[27]=0x000A=10=DB10[其实这里写什么都可以,因为input不属于DB块], A[28]=0x82=读取的数据类型为Output,A[29]~A[31]=0x000000=0=08=读取偏移量offset(bit为单位) ) 03 00 00 1F 02 F0 80 32 01 00 00 00 01 00 0E 00 00 04 01 12 0A 10 02 00 02 00 0A 82 00 00 00 PLC回复报文: (B[3]~B[4]=0x001B=27=回复报文总长度, B[12]~B[13]=0x0002=序列号,B[16]~B[17]=0x0068=104=读取请求count(100)+4 B[24]~B[25]=0x0320=1008=请求数据长度(bit为单位), B[26]~B[27]=数据值) 03 00 00 1B 02 F0 80 32 03 00 00 00 01 00 02 00 06 00 00 04 01 FF 04 00 10 05 00 读demo8: 西门子1200 读取flag两个byte: PC发出报文: 03 00 00 1F 02 F0 80 32 01 00 00 05 65 00 0E 00 00 04 01 12 0A 10 02 00 02 00 09 83 00 00 00 PLC回复报文: (B[3]~B[4]=0x001B=27=回复报文总长度, B[12]~B[13]=0x0565=序列号,B[16]~B[17]=0x0006=6=读取请求count(2)+4 B[24]~B[25]=0x0010=2*8=请求数据长度(bit为单位), B[26]~B[27]=数据值) 03 00 00 1B 02 F0 80 32 03 00 00 05 65 00 02 00 06 00 00 04 01 FF 04 00 10 FF 17 根据以上8个报文的demo,摸索出大致规律如下(未必完全正确,但是应付项目可以了); A[1]~A[2]: 03 00 固定报文头; A[3]~A[4]: 00 1F 整个读取请求长度为0x1F= 31 ; A[5]~A[11]: 02 F0 80 32 01 00 00 固定6个字节; A[12]~A[13]: 两个字节,标识序列号,回复报文相同位置和这个完全一样;范围是0~65535; A[14]~A[23]:00 0E 00 00 04 01 12 0A 10 02 固定10个字节 A[24]~A[25]:两个字节,访问数据的个数,以byte为单位; A[26]~A[27]: DB块的编号,比如DB50, 就是0x32=50, 两个字节,范围是0~65535(也许是一个1个字节,因为没有设置估DB255以上的数据块,所以不知道到底是几个字节,姑且认为是2个字节); A[28] : 访问数据块的类型:0x81-input ,0x82-output ,0x83-flag , 0x84-DB(这个最常见); A[29]~A[31]: 访问DB块的偏移量offset (地址+1以byte为单位); 3个字节,范围是0~16777216(一般 用不到这么大) 程序设计的时候,其实主要关注最后4个信息,即: A[24]~A[25]: 访问byte个数 A[26]~A[27]: DB块编号 A[28] : 数据块类型 4.A[29]~A[31] :访问地址偏移量;相当于首地址编号 B[1]~B[2]: 03 00 固定报文头 B[3]~B[4]: 整个读取回复报文长度:25+读取长度; B[5]~B[11]: 02 F0 80 32 03 00 00 固定6个字节,和读取请求相同的位置几乎一样,就 B[9]=0x03 ;A[9]=0x01; B[12]~B[13]: 两个字节,标识序列号,回复报文相同位置和这个完全一样;范围是0~65535; B[14]~B[15]: 两个字节,固定为00 02;对应读取位置是 00 0E;正好 02+0E=10 ;有点补码的感觉,其实不需要关注规律,反正是固定的; B[16]~B[17]:两个字节,=请求读取的字节数+4; B[18]~B[23]:6个字节,固定为:00 00 04 01 FF 04 ; B[24]~B[25]:两个字节, 请求访问的byte个数*8 ;其实就是以二进制为单位的个数;由此可以看出,一口气最多访问的地址个数是8192; B[26]~ 最后一个 :以offset作为首地址,所对应的各个byte的值; 程序设计的时候,其实只要关注两个信息: 1.校验B[3]~B[4]:校验长度正确; 2.B[26]~最后一个 :获取对应的值; 到这里读的处理就算结束了;几个小注意点: 对于不同信号的PLC,除了初始化的CPUSolt不同;正常读/写指令是一样的; 2.读的时候,都是以byte为单位的,如果程序只需要bit,那么还是以Byte为单位去读,将读出的部分按bit再去分解; 3.flag类型到底是什么,不是很清楚,有点类似三菱里的M点;这个也不需要去深究,一般项目里主要就是用DB块; 4.读取的长度如果是N(以byte为单位),那么返回的长度就是N8(以bit为单位);怎么判断长度是否要8;主要看后面是不是紧挨着数据, 如果是数据,就需要8;offset都是以bit为单位的; 5.正常读的操作都是DB块,所以在A[26]~A[27]这个字节写入DB块的编号,但是对于input,output,flags这三个类型,是不需要数据块编号的, 不过我们可以随便写一个DB编号; 4.写操作 写demo1: 西门子1200 写 db10.WORD18=0xFFFE=65534; 也就是: DB10.b18=0xFF; DB10.B19=0xFE; PC发出报文: (A[3]~A[4]=0x0025=37=读取报文总长度, A[12]~A[13]=0x0005=序列号,A[16]~A[17]=0x06=写入byte个数(2)+4 , A[23]=0x02=写入方式为byte, A[24]~A[25]=0x0002=2=写入个数count; A[26]~A[27]=0x000A=10=DB10,A[28]=0x84=写入的数据类型为DB块,A[29]~A[31]=0x000090=144=188=读取偏移量offset(bit为单位), A[32]~A[33]=0x0004=写入方式为Byte , A[34]~A[35]=0x0010=28=写入byte的个数(bit为单位) ,A[36]~A[37]= 写入数据) 03 00 00 25 02 F0 80 32 01 00 00 00 05 00 0E 00 06 05 01 12 0A 10 02 00 02 00 0A 84 00 00 90 00 04 00 10 FF FE PLC回复报文: ( B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入) 03 00 00 16 02 F0 80 32 03 00 00 00 05 00 02 00 01 00 00 05 01 FF 写demo2: 1200 写入 DB10. X2.6=1 (这里是按bit写入) PC发出报文: (A[3]~A[4]=0x0024=36=读取报文总长度, A[12]~A[13]=0x0008=序列号,A[16]~A[17]=0x05=写入bit个数(1)+4 A[26]~A[27]=0x000A=10=DB10,A[28]=0x84=写入的数据类型为DB块,A[29]~A[31]=0x000016=22=28+6=读取偏移量offset( bit为单位) A[32]~A[33]=0x0003=写入方式为bit , A[34]~A[35]=0x0001=写入bit的个数(bit为单位) ,A[36]= 写入数据[0或1]) 03 00 00 24 02 F0 80 32 01 00 00 00 08 00 0E 00 05 05 01 12 0A 10 01 00 01 00 0A 84 00 00 16 00 03 00 01 01 PLC回复报文: ( B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入) 03 00 00 16 02 F0 80 32 03 00 00 00 08 00 02 00 01 00 00 05 01 FF 写demo3: 1200 写 入:output0=4 PC发出报文: (A[3]~A[4]=0x0024=36=读取报文总长度, A[12]~A[13]=0x0008=序列号,A[16]~A[17]=0x05=写入byte个数(1)+4 ,A[23]=0x02=写入方式为byte,A[24]~A[25]=0x0001=1=写入个数count; A[26]~A[27]=0x0001=DB1(因为是output,所以DB块编号无所谓),A[28]=0x82=写入的数据类型为output,A[29]~A[31]=0x000000=读取偏移量offset( bit为单位) A[32]~A[33]=0x0004=写入方式为byte , A[34]~A[35]=0x0008=1*8=写入byte的个数 ,A[36]= 写入数据) 03 0000 24 02 F0 80 32 01 00 00 00 08 00 0E 00 05 05 01 12 0A 10 02 00 01 00 01 82 00 00 00 00 04 00 08 04 PLC回复报文: ( B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入) 03 00 00 16 02 F0 80 32 03 00 00 00 08 00 02 00 01 00 00 05 01 FF 写demo4: 1200 写 输入:output 0.3=1 PC发出报文: (A[3]~A[4]=0x0024=36=写入报文总长度, A[12]~A[13]=0x0003=序列号,A[16]~A[17]=0x05=写入bit个数(1)+4A[23]=0x01=写入方式为bit,A[24]~A[25]=0x0001=1=写入个数count; A[26]~A[27]=0x000A=10=DB10(因为是output,所以DB块编号无所谓),A[28]=0x82=写入的数据类型为output,A[29]~A[31]=0x000003=读取偏移量offset( bit为单位) A[32]~A[33]=0x0003=写入方式为bit , A[34]~A[35]=0x0001=写入bit的个数(bit为单位) ,A[36]= 写入数据[0或1]) 03 00 00 24 02 F0 80 32 01 00 00 00 03 00 0E 00 05 05 01 12 0A 10 01 00 01 00 01 82 00 00 03 00 03 00 01 01 PLC回复报文: ( B[12]~B[13]=0x0565=序列号,最后一个B[14]=0xFF表示写入) 03 00 00 16 02 F0 80 32 03 00 00 00 03 00 02 00 01 00 00 05 01 FF 根据以上4个报文的demo,摸索出大致规律如下(未必完全正确,但是应付项目可以了); A[1]~A[2]: 03 00 固定报文头; A[3]~A[4]: 整个报文长度:35+写入长度; A[5]~A[11]: 02 F0 80 32 01 00 00 固定6个字节(和读取的完全一样) A[12]~A[13]: 两个字节,标识序列号,回复报文相同位置和这个完全一样;范围是0~65535; A[14]~A[15]:00 0E 固定2个字节; A[16]~A[17]:写入长度+4; A[18]~A[22]: 05 01 12 0A 10 固定5个自己 A[23] : 写入方式: 01-按bit写入; 02-按byte写入; A[24]~A[25]:两个字节,写入数据的个数(可能是byte或bit, 按A[23]来区分) A[26]~A[27]: DB块的编号 A[28] : 写入数据块的类型:0x81-input ,0x82-output ,0x83-flag , 0x84-DB(这个最常见); A[29]~A[31]: 写入DB块的偏移量offset (地址+1以byte为单位); 3个字节,范围是0~16777216(一般 用不到这么大)A[32]~A[33]:写入方式为: 03-按bit写入; 04-按byte写入; A[34]~A[35]:写入bit的个数(bit为单位) A[36]~最后 : 连续的写入值; B[1]~B[2]: 03 00 固定报文头; B[14]: FF 标识写入正常; 到这里,初始化,读,写 这3种方式都摸索完了,未必都正确,应付开发应该绰绰余裕了; 在接下来的时间里,就需要把这些规律变成相应的程序; 注意点: 1.写入可以按byte和bit两种方法去操作; 2.对于byte,可以一口气写连续多个byte, 理论上一条指令连续写bit也可以,但是实践下来,发现有问题,所以对于bit操作,我们就一个一个写吧; 原文链接:http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fm.baidu.com%2Fmip%2Fc%2Fwww.360doc.cn%2Fmip%2F763580999.html&urlrefer=7ba98d112ba5d043df796dc638d1bd41
FX2N 服务器版, .版本 2 .支持库 sock .支持库 iext .支持库 dp1 .支持库 spec .程序集 窗口程序集_启动窗口 .程序集变量 数组号_A, 整数型 .程序集变量 数组号_B, 整数型 .程序集变量 在线, 整数型 .程序集变量 读类, 整数型 .程序集变量 数组变化, 整数型 .程序集变量 读类YD, 逻辑型 .程序集变量 端口2_端口号, 文本型 .子程序 __启动窗口_创建完毕, , , 1.启动程序前计算好运算时要的数据,2.将保持的配置读取出来并使用 .局部变量 s, 整数型 .局部变量 IP地址, 文本型, , "10" .局部变量 ss, 整数型 IP地址 = 取本机IP () s = 取数组成员数 (IP地址) .如果 (取数组成员数 (IP地址) > 1) .计次循环首 (s, ss) 组合框11.加入项目 (IP地址 [ss], ss) .计次循环尾 () 组合框11.内容 = “本机有” + 到文本 (ss - 1) + “个IP地址” .否则 组合框11.加入项目 (IP地址 [1], ) 组合框11.现行选中项 = 0 .如果结束 软件设置读 () 端口A初始化 () 串口A读配置 () 串口B读配置 () 判断端口启用 () ' 权限管理_读配置项 ()’暂时不用这个(全靠在线服务) ' 线程操作许可证1 = 创建进入许可证 () ' -----------------------------------------规划 端口数据进程 ' 线程操作许可证2 = 创建进入许可证 () ' ---------------------------------------- 规划 端口数据进程 置托盘图标 (#托盘图标, “祥哥智能家居服务系统”) 运行目录 = 取运行目录 () + “\” ' 动画框1.添加监视键 (200, #回车键) ' 留着做快捷键用-------------------- .子程序 _动画框1_监视键被按下 .参数 键代码, 整数型 .参数 功能键状态, 整数型 ' 如果真 (键代码 = #回车键)----------------------------------------------- ' _按钮1_被单击 ()--------------------------------------------------------- .子程序 _按钮3_被单击, , , 端口A 连接 按钮 .如果 (选择框4.选中 = 真) .如果 (组合框6.现行选中项 = 组合框1.现行选中项) 编辑框_串口动作.内容 = “端口号已被占用!” .否则 端口A初始化 () .如果 (外形框_端口A.填充颜色 = #灰色) 端口1.启动 () 端口1.发送数据 ({ 5 }) .否则 端口1.停止 () 外形框_端口A.填充颜色 = #灰色 编辑框_串口动作.内容 = “端口已关闭” 连续赋值 (假, 组合框1.禁止, 组合框2.禁止, 组合框3.禁止, 组合框4.禁止, 组合框5.禁止) 按钮3.标题 = “连接” .如果结束 .如果结束 .否则 端口A初始化 () .如果 (外形框_端口A.填充颜色 = #灰色) 端口1.启动 () 端口1.发送数据 ({ 5 }) .否则 端口1.停止 () 外形框_端口A.填充颜色 = #灰色 编辑框_串口动作.内容 = “端口已关闭” 连续赋值 (假, 组合框1.禁止, 组合框2.禁止, 组合框3.禁止, 组合框4.禁止, 组合框5.禁止) 按钮3.标题 = “连接” .如果结束 .如果结束 .子程序 _端口1_数据到达 .参数 数据字节值, 整数型 .如果真 (数据字节值 = 21) ' ----------------回传数据=21 为拒绝接收数据。或设备不能识别的数据 编辑框_串口动作.内容 = “拒绝接收,或 PLC无法识别” .如果真结束 .如果真 (数据字节值 = 6) 外形框_端口A.填充颜色 = #绿色 按钮3.标题 = “断开” 连续赋值 (真, 组合框1.禁止, 组合框2.禁止, 组合框3.禁止, 组合框4.禁止, 组合框5.禁止) 编辑框_串口动作.内容 = “端口A连接成功” 采集数据 () .如果真结束 .如果真 (数据字节值 = 2) ' -------------------回传数据=2 为通讯报文的开始 M0 = 真 数组号_A = 1 .如果真结束 .如果真 (M0 = 真 且 数据字节值 ≥ 48) ' --------------------------在通讯报文的开始码开始接收一直到出现结束码、 串口数据A [数组号_A] = 字符 (数据字节值) 数组号_A = 数组号_A + 1 .如果真结束 .如果真 (数据字节值 = 3) ' -------------------回传数据=3 为通讯报文的结束, 结束后还有2位和验证的验证码,在这里我们放弃验证,其实在通讯条件非常好的时候根本不需要验证。 数组号_A = 1 M0 = 假 .判断开始 (读类YD = 假) 交换D位置 () 读类YD = 真 .默认 交换Y位置 () 读类YD = 假 .判断结束 采集数据 () .如果真结束 .子程序 端口A初始化, , , 将全局变量里的端口数据赋给端口,准备启动 _组合框1_列表项被选择 () _组合框2_列表项被选择 () _组合框3_列表项被选择 () _组合框4_列表项被选择 () _组合框5_列表项被选择 () 端口1.端口号 = 端口A端口号 端口1.波特率 = 端口A波特率 端口1.数据位数 = 端口A数据位数 端口1.停止位数 = 端口A停止位 端口1.奇偶校验方案 = 端口A奇偶验证 .子程序 端口B初始化, , , 将全局变量里的端口数据赋给端口,准备启动 _组合框6_列表项被选择 () _组合框7_列表项被选择 () _组合框8_列表项被选择 () _组合框9_列表项被选择 () _组合框10_列表项被选择 () 端口2.端口号 = 端口B端口号 端口2.波特率 = 端口B波特率 端口2.数据位数 = 端口B数据位数 端口2.停止位数 = 端口B停止位 端口2.奇偶校验方案 = 端口B奇偶验证 .子程序 _组合框1_列表项被选择, , , ========================================↑====================================== 端口A端口号 = 组合框1.现行选中项 + 1 .子程序 _组合框2_列表项被选择, , , ========================================‖====================================== 端口A波特率 = 到整数 (组合框2.取项目文本 (组合框2.现行选中项)) .子程序 _组合框3_列表项被选择, , , ===================================端口 A 设置================================= 端口A数据位数 = 到整数 (组合框3.取项目文本 (组合框3.现行选中项)) .子程序 _组合框4_列表项被选择, , , ========================================‖====================================== 端口A停止位 = 组合框4.现行选中项 + 1 .子程序 _组合框5_列表项被选择, , , ========================================↓====================================== 端口A奇偶验证 = 组合框5.现行选中项 .子程序 _组合框6_列表项被选择, , , ========================================↑====================================== 端口B端口号 = 组合框6.现行选中项 + 1 .子程序 _组合框7_列表项被选择, , , ========================================‖====================================== 端口B波特率 = 到整数 (组合框7.取项目文本 (组合框7.现行选中项)) .子程序 _组合框8_列表项被选择, , , ===================================端口 B 设置================================= 端口B数据位数 = 到整数 (组合框8.取项目文本 (组合框8.现行选中项)) .子程序 _组合框9_列表项被选择, , , ========================================‖====================================== 端口B停止位 = 组合框9.现行选中项 + 1 .子程序 _组合框10_列表项被选择, , , =======================================↓====================================== 端口B奇偶验证 = 组合框10.现行选中项 .子程序 __启动窗口_将被销毁 ' 如果 (信息框 (“你更改了软件设置,是否保存设置?”, 1, “设置保存”, ) = 0) '留着写 检查已经连接用户 是否要关闭服务器 ' 删除进入许可证 (线程操作许可证1) ' 删除进入许可证 (线程操作许可证2) 串口A写配置 () 串口B写配置 () 软件设置写 () ' 权限管理_写配置项 ()’暂时用不到这个功能,全部在线服务 .子程序 _选择框3_被单击 判断端口启用 () .子程序 _选择框4_被单击 判断端口启用 () .子程序 _退出_被选择 __启动窗口_将被销毁 () 销毁 () .子程序 _时钟_服务心跳_周期事件 ' 如果和判断时间比较下来,还是如果比较快些,但相差不大、--------------------------预留 最终要去掉判断 扫描执行区 () .子程序 _系统配置_被选择 载入 (窗口_系统设置, _启动窗口, 真) .子程序 _超级列表框1_右键单击表项, , , 线判断有没有选择,再弹出菜单,这样菜单不会与实际记录有偏差 .局部变量 项目数, 整数型 现行选中项 = 超级列表框1.现行选中项 ' 再次赋值给 “现行选中项”----在删除超级列表框剩余的最后一项时,超级列表框不能返回当前的现行选中项。 项目数 = 超级列表框1.取表项数 () .如果 (项目数 = 0) 删除用户.禁止 = 真 允许控制.禁止 = 真 全部允许.禁止 = 真 全部不允许.禁止 = 真 另存为记录.禁止 = 真 导入记录.禁止 = 假 .否则 .如果 (现行选中项 ≠ -1) .如果 (超级列表框1.取标题 (现行选中项, 4) = “真”) 允许控制.选中 = 真 .否则 允许控制.选中 = 假 .如果结束 删除用户.禁止 = 假 允许控制.禁止 = 假 全部允许.禁止 = 真 全部不允许.禁止 = 真 另存为记录.禁止 = 真 导入记录.禁止 = 真 .否则 删除用户.禁止 = 真 允许控制.禁止 = 真 全部允许.禁止 = 假 全部不允许.禁止 = 假 另存为记录.禁止 = 假 导入记录.禁止 = 假 .如果结束 .如果结束 弹出菜单 (用户管理右键菜单, 取鼠标水平位置 (), 取鼠标垂直位置 ()) .子程序 _允许控制_被选择 .局部变量 选中的客户, 文本型 选中的客户 = 超级列表框1.取标题 (现行选中项, 1) + “:” + 超级列表框1.取标题 (现行选中项, 3) .如果 (允许控制.选中 = 假) 超级列表框1.置标题 (现行选中项, 4, “真”) 允许控制.选中 = 真 服务器_权限_控制.发送数据 (选中的客户, “真”, ) .否则 允许控制.选中 = 假 超级列表框1.置标题 (现行选中项, 4, “假”) 服务器_权限_控制.发送数据 (选中的客户, “假”, ) .如果结束 .子程序 _超级列表框1_当前表项被改变 现行选中项 = 超级列表框1.现行选中项 ' 进入许可区 (线程操作许可证1)'在这2个命令当中写多线程程序,写完一定注意要退出许可区 ' 退出许可区 (线程操作许可证2) ' 启动线程 (, , ) .子程序 _关于_被选择 信息框 (“ 开发家用控制系统1.68版欢迎使用!”, 0, “关于软件”, _启动窗口) .子程序 __启动窗口_托盘事件 .参数 操作类型, 整数型 .如果真 (操作类型 = #单击右键) 弹出托盘菜单 (托盘菜单) .如果真结束 .如果真 (操作类型 = #单击左键) .如果 (_启动窗口.可视 = 假) _启动窗口.可视 = 真 _启动窗口.位置 = 1 .否则 _启动窗口.可视 = 假 .如果结束 .如果真结束 .子程序 _隐藏界面_被选择 _启动窗口.可视 = 假 .子程序 _显示界面_被选择 _启动窗口.可视 = 真 .子程序 _退出系统_被选择 _退出_被选择 () .子程序 _删除用户_被选择 服务器_注册发送.断开客户 (超级列表框1.取标题 (现行选中项, 1) + “:” + 超级列表框1.取标题 (现行选中项, 2)) 超级列表框1.删除表项 (现行选中项) .如果 (超级列表框1.取表项数 () = 0 且 外形框_端口A.填充颜色 = #绿色) _按钮3_被单击 () .否则 .如果结束 .子程序 _按钮4_被单击 .如果 (选择框3.选中 = 真) .如果 (组合框6.现行选中项 = 组合框1.现行选中项) 编辑框_串口动作.内容 = “端口号已被占用!” .否则 端口B初始化 () .如果 (外形框_端口B.填充颜色 = #灰色) 端口2.启动 () 端口2.发送数据 ({ 5 }) .否则 端口2.停止 () 外形框_端口B.填充颜色 = #灰色 编辑框_串口动作.内容 = “串口B 端口已关闭” 连续赋值 (假, 组合框6.禁止, 组合框7.禁止, 组合框8.禁止, 组合框9.禁止, 组合框10.禁止) .如果结束 .如果结束 .否则 端口B初始化 () .如果 (外形框_端口B.填充颜色 = #灰色) 端口2.启动 () 端口2.发送数据 ({ 5 }) .否则 端口2.停止 () 外形框_端口B.填充颜色 = #灰色 编辑框_串口动作.内容 = “端口已关闭” 连续赋值 (假, 组合框6.禁止, 组合框7.禁止, 组合框8.禁止, 组合框9.禁止, 组合框10.禁止) .如果结束 .如果结束 .子程序 _端口2_数据到达 .参数 数据字节值, 整数型 .如果真 (数据字节值 = 21) ' ----------------回传数据=21 为拒绝接收数据。或设备不能识别的数据 编辑框_串口动作.内容 = “拒绝接收,或 PLC无法识别” .如果真结束 .如果真 (数据字节值 = 6) 编辑框_串口动作.内容 = “端口A连接成功” ' -----------------回传数据=5 为连接成功,运行通讯 外形框_端口B.填充颜色 = #绿色 连续赋值 (真, 组合框6.禁止, 组合框7.禁止, 组合框8.禁止, 组合框9.禁止, 组合框10.禁止) .如果真结束 .如果真 (数据字节值 = 2) ' -------------------回传数据=2 为通讯报文的开始 重定义数组 (串口数据B, 假, 76) ' 接收前先清空容器内的数组数据 M10 = 真 数组号_B = 0 .如果真结束 .如果真 (M10 = 真 且 数据字节值 ≥ 48) ' --------------------------在通讯报文的开始码开始接收一直到出现结束码、 数组号_B = 数组号_B + 1 串口数据B [数组号_B] = 数据字节值 .如果真结束 .如果真 (数据字节值 = 3) ' -------------------回传数据=3 为通讯报文的结束, 结束后还有2位和验证的验证码,在这里我们放弃验证,其实在通讯条件非常好的时候根本不需要验证。 数组号_B = 0 .如果真结束 .子程序 _另存为记录_被选择 .局部变量 i .局部变量 ii .局部变量 文本, 文本型 通用对话框1.标题 = “保存为加密过的dat文件” 通用对话框1.初始目录 = 运行目录 通用对话框1.类型 = 1 通用对话框1.打开 () .计次循环首 (超级列表框1.取表项数 (), i) .计次循环首 (超级列表框1.取列数 (), ii) 文本 = 文本 + 超级列表框1.取标题 (i - 1, ii - 1) + “|” .计次循环尾 () 文本 = 文本 + #换行符 .计次循环尾 () 写到文件 (通用对话框1.文件名, 加密数据 (到字节集 (文本), “菊花”, 2)) .子程序 _导入记录_被选择 .局部变量 text, 文本型, , , 先把文本读出来(可以在读写文件前加密) .局部变量 行数, 整数型, , , 读出的文件有几行 .局部变量 每行文本, 文本型, , "0", 分割文本放在变量数组里。有机个数组就有几个列 .局部变量 列数, 整数型, , , 没行里有多少列 .局部变量 g, 整数型 .局部变量 gg, 整数型 .局部变量 行内每组文本, 文本型, , "0" 通用对话框1.标题 = “打开一个已经加密过的dat文件,非加密会出错。” 通用对话框1.类型 = 0 ' 通用对话框1.打开 () .如果真 (通用对话框1.打开 ()) text = 到文本 (解密数据 (读入文件 (通用对话框1.文件名), “菊花”, 2)) 超级列表框1.全部删除 () 每行文本 = 分割文本 (text, #换行符, ) ' //text 这个参数可以跟改为 编辑框1.内容 或存放数据的容器、变量 行数 = 取数组成员数 (每行文本) .计次循环首 (行数, g) 超级列表框1.插入表项 (, , , , , ) 行内每组文本 = 分割文本 (每行文本 [g], “|”, ) 列数 = 取数组成员数 (行内每组文本) .计次循环首 (列数, gg) 超级列表框1.置标题 (g - 1, gg - 1, 行内每组文本 [gg]) .计次循环尾 () .计次循环尾 () .如果真结束 .子程序 _时钟_启动步骤_周期事件, , , 留着删除文件用,加密和解密数据到配置项后删除文件 启动步骤 = 启动步骤 + 1 .如果真 (启动步骤 = 1) .如果真结束 .如果真 (启动步骤 = 2) .如果真 (_启动窗口.端口A自动连接.选中 = 真 且 外形框_端口A.填充颜色 = #灰色) _按钮3_被单击 () .如果真结束 .如果真结束 .如果真 (启动步骤 = 3) .如果真结束 .如果真 (启动步骤 = 4) .如果真结束 .如果真 (启动步骤 = 5) .如果真结束 .如果真 (启动步骤 = 6) .如果真结束 .如果真 (启动步骤 = 7) .如果真结束 .如果真 (启动步骤 = 8) .如果真结束 .如果真 (启动步骤 = 9) ' 留着后期做注册和使用全等等。。。。 启动步骤 = 0 .如果真结束 .子程序 _端口A自动连接_被选择 .如果 (端口A自动连接.选中 = 真) 端口A自动连接.选中 = 假 A口自起设置 () .否则 端口A自动连接.选中 = 真 A口自起设置 () .如果结束 .子程序 _端口B自动连接_被选择 .如果 (端口B自动连接.选中 = 真) 端口B自动连接.选中 = 假 B口自起设置 () .否则 端口B自动连接.选中 = 真 B口自起设置 () .如果结束 .子程序 采集数据 .判断开始 (读类YD = 假) ' 读 Y ' 端口1.启动 () 端口1.发送数据 ({ 2, 48, 49, 48, 48, 48, 49, 48, 3, 53, 53 }) ' 读出D0 ~ D7 共8个D的值 返回32个数组(一个D 用4个字节来代表 最大 32767 ) .默认 ' 端口1.启动 () ' 端口1.发送数据 ({ 2, 69, 48, 48, 48, 49, 56, 48, 48, 70, 3, 69, 55 }) ' 读Y0开始120个点 返回30个数组 端口1.发送数据 ({ 2, 69, 48, 48, 48, 49, 56, 48, 49, 48, 3, 68, 50 }) ' 读Y0开始128个点 返回32个数组 .判断结束 ' _启动窗口.端口1.发送数据 ({ 2, 69, 48, 48, 48, 49, 56, 48, 49, 69, 3, 69, 55 }) ' 读Y0开始240个点 ' D的计算方式 地址算法:address = address * 2 + 1000h ((ˇ?ˇ) 想要监控的D号乘以2转换为16进制再加上1000(因为1000是16进制的)得到一个16进制的值) ' 端口1.发送数据 ({ 2, 48, 49, 48, 48, 48, 50, 48, 3, 53, 54 }) ' 已经测试 读出D0 ~ D15 共16个D的值 返回64个数组(一个D 用4个字节来代表 最大 32767 ) ' 端口1.发送数据 ({ 2, 69, 48, 48, 48, 48, 48, 48, 50, 54, 3, 68, 48 }) ' 已经测试 一下子可以监控 304个M点 ' 端口1.发送数据 ({ 2, 69, 48, 48, 48, 48, 48, 48, 49, 51, 3, 67, 67 }) ' 监控 152个M点 返回38个数组。易语言 70毫秒扫描一次时进过测试没问题 ' 端口1.发送数据 ({ 2, 69, 48, 48, 48, 49, 56, 48, 48, 70, 3, 69, 55 }) ' 读Y0开始120个点 返回30个数组 ' 端口1.发送数据 ({ 2, 48, 49, 48, 48, 48, 49, 48, 3, 53, 53 }) ' 读出D0 ~ D7 共8个D的值 返回32个数组(一个D 用4个字节来代表 最大 32767 ) .子程序 _超级列表框1_被双击, 逻辑型 .参数 横向位置, 整数型 .参数 纵向位置, 整数型 .参数 功能键状态, 整数型 .如果 (超级列表框1.现行选中项 ≠ -1) 载入 (窗口_单独聊天, _启动窗口, 假) ' 调试输出 (超级列表框1.现行选中项) ' 预留 读出被选中项的内容,或地址直接聊天。或发响应的指定命令 .否则 .如果结束 .子程序 _服务器_注册发送_客户进入 .局部变量 客户字头, 文本型 .局部变量 客户IP和端口, 文本型, , "2" .局部变量 插在这里, 整数型 .局部变量 查找后返回值, 整数型 客户字头 = 服务器_注册发送.取回客户 () 客户IP和端口 = 分割文本 (客户字头, “:”, 2) 查找后返回值 = 超级列表框1.查找表项 (客户IP和端口 [1], 0, , 1) .如果 (查找后返回值 = -1) 插在这里 = 超级列表框1.插入表项 (, , , , , ) 超级列表框1.置标题 (插在这里, 1, 客户IP和端口 [1]) 超级列表框1.置标题 (插在这里, 2, 客户IP和端口 [2]) 超级列表框1.置标题 (插在这里, 3, 端口2_端口号) .如果真 (外形框_端口A.填充颜色 = #灰色) _按钮3_被单击 () .如果真结束 .否则 超级列表框1.置标题 (查找后返回值, 2, 客户IP和端口 [2]) 超级列表框1.置标题 (插在这里, 3, 端口2_端口号) .如果结束 .子程序 _服务器_注册发送_客户离开 .局部变量 客户字头, 文本型 .局部变量 客户IP和端口, 文本型, , "2" .局部变量 查找后返回值, 整数型 .局部变量 几个客户, 整数型 客户字头 = 服务器_注册发送.取回客户 () 客户IP和端口 = 分割文本 (客户字头, “:”, 2) 查找后返回值 = 超级列表框1.查找表项 (客户IP和端口 [1], 0, , 1) .如果真 (查找后返回值 ≠ -1) 超级列表框1.删除表项 (查找后返回值) .如果真结束 几个客户 = 超级列表框1.取表项数 () .如果真 (几个客户 = 0 且 外形框_端口A.填充颜色 = #绿色) _按钮3_被单击 () .如果真结束 .子程序 _服务器_权限_控制_数据到达 .局部变量 登陆口取回数据, 文本型 .局部变量 插在这里, 整数型 .局部变量 查找后返回值, 整数型 .局部变量 客户字头, 文本型 .局部变量 客户IP和端口, 文本型, , "2" .局部变量 网卡硬件号, 文本型 .局部变量 查找权限, 文本型 .局部变量 识别码, 文本型 .局部变量 M多少, 整数型 .局部变量 位地址十六, 文本型 .局部变量 s, 整数型 .局部变量 待发, 整数型, , "4" .局部变量 和, 文本型 .局部变量 验证码_前, 整数型 .局部变量 验证码_后, 整数型 .局部变量 机器名称, 文本型 登陆口取回数据 = 到文本 (服务器_权限_控制.取回数据 ()) 客户字头 = 服务器_权限_控制.取回客户 () 客户IP和端口 = 分割文本 (客户字头, “:”, 2) 查找后返回值 = 超级列表框1.查找表项 (客户IP和端口 [1], 0, 真, 1) 识别码 = 取文本左边 (登陆口取回数据, 3) .判断开始 (识别码 = “3%$”) ' ----------------- 通过网卡硬件号来发放控制权 网卡硬件号 = 取文本右边 (登陆口取回数据, 17) 机器名称 = 取文本中间 (登陆口取回数据, 4, 取文本长度 (登陆口取回数据) - 20) 超级列表框1.置标题 (查找后返回值, 0, 机器名称) 超级列表框1.置标题 (查找后返回值, 5, 网卡硬件号) 超级列表框1.置标题 (查找后返回值, 3, 客户IP和端口 [2]) 查找权限 = 超级列表框1.取标题 (查找后返回值, 4) .如果 (查找权限 ≠ “”) 服务器_权限_控制.发送数据 (客户字头, 查找权限, ) .否则 服务器_权限_控制.发送数据 (客户字头, “空”, ) .如果结束 .判断 (识别码 = “$%M”) 端口1.停止 () 延迟 (50) ' 数据通信赌赛调整。低于20有信道赌赛现象 M多少 = 到整数 (取文本右边 (登陆口取回数据, 4)) + 2048 ' Mn=十进制整数 也就是 M多少就是十进制的多少。如M123,就是123,发送时要加上十六进制800=十进制2048.实际发送的是123转换为十六进制“7B”加上800= “87B” 发送时的顺序是 “7”“B”“0”“8”0是千位没有补零,M强on 最大为M1023,目前无其他这里 位地址十六 = 取十六进制文本 (M多少) ' 这里把加上800后的实际位地址转换为十六进制文本 .计次循环首 (3, s) 待发 [s] = 十六转ASCLL_10 (取文本中间 (位地址十六, s, 1)) .计次循环尾 () 和 = 取文本右边 (取十六进制文本 (待发 [1] + 待发 [2] + 待发 [3] + 106), 2) 验证码_前 = 十六转ASCLL_10 (取文本左边 (和, 1)) 验证码_后 = 十六转ASCLL_10 (取文本右边 (和, 1)) 端口1.启动 () 端口1.发送数据 (字符 (2) + 字符 (55) + 字符 (待发 [2]) + 字符 (待发 [3]) + 字符 (48) + 字符 (待发 [1]) + 字符 (3) + 字符 (验证码_前) + 字符 (验证码_后)) .判断 (识别码 = “$%R”) .默认 .判断结束 .子程序 _全部允许_被选择 .局部变量 项目数, 整数型 .局部变量 s, 整数型 .局部变量 ss, 整数型 项目数 = 超级列表框1.取表项数 () .计次循环首 (项目数, s) ss = s - 1 超级列表框1.置标题 (ss, 4, “真”) .计次循环尾 () .子程序 _全部不允许_被选择 .局部变量 项目数, 整数型 .局部变量 s, 整数型 .局部变量 ss, 整数型 项目数 = 超级列表框1.取表项数 () .计次循环首 (项目数, s) ss = s - 1 超级列表框1.置标题 (ss, 4, “假”) .计次循环尾 ()
????????????????? 吧主什么时候创建的此吧?为什么没人来发帖呢?
公司现状 待遇:国家最低标准工资 福利:在你生日的时候可以拿到一个小蛋糕(这是今年刚开始的)其他没了。 工作时间:8:00~20:00 ,20:00~8:00 ;生产部门夜班要加班到第二天中午。白班到夜里12点。 结算:按5天8小时算1820算,平时1.5倍。周末2倍。 保障:5险 没有公积金(此项极不平等,劳工部交公积金,办公室人员5险1金) 新来公司的都会有异样感觉。卫生问题全靠消防水冲。每年不知道要给国家浪费多少水。
大荣怎么样 大荣怎么样
htc m8t升级安卓6.0后体会 2016年3月11日晚,感谢系统为我推送了升级安卓6.0的信息。由于之前的升级种种。。。。你懂的。。。。。。这次我没有犹豫就点了下载升级了。因为我是个强迫症。不想看到一直被提示的信息又去不掉。这能在xxx的淫威之下。大概20多分钟之后划开屏幕体验“快感”。点开微信"发现"那2字上有红点。(强迫症嘛)马上点开看,原来是朋友圈里有人转载的视频。就点开看了。xx出现了。全屏播放还能有状态栏。立马打了400-821-8998。就在这时惊喜有出现了,拨号界面让人作呕,连以前的切换英文键盘都没了。(本人联系人有点多,每次都是直接输入姓名的第一个字母准确的找到某某人的)。郁闷之余到处找相关设置,还发现多了好多垃圾功能都没选择的。比如有键盘颜色。打开是空的。(设置-个性化设置-拨号面板颜色;和键盘颜色里都是空的)。通讯录里还多了个黄页,不知道哪个鬼要用到这个东西。还有就是。本来就是大下巴。还多了个隐藏键。难道以前自动的不好吗?多余中的多余。在超级不爽的时候马上打了400-821-8998进过详细询问,回答是:“很抱歉给您带来了不便,这个无法设置。请问你还有其他疑问吗?”。。。。。。。。。。。。。。。。。。。 #¥%……&*(……%¥#@#¥%……&*
为什么为什么 为什么为什么又成功了呢
ddgsf asddfasdgasgassssssssssssss
苏州数字电视 连菜单都带广告,太贱了。花钱买的电视节目,都是广告。
1
下一页