☰
CODESYS V3.5入门指南:从环境搭建到数据采集的完整实践笔记
2026/10/1 23:49:01 网站建设 项目流程

你有没有过这种经历:现场三台设备,一台用三菱,一台用西门子,还有一台国产机器,打开程序一看,软件又是一个新的。这几年我最大的感受是,PLC开发的“语言分裂”正在被CODESYS一点点治疗。所谓CODESYS,简单说就是一套符合IEC 61131-3标准的PLC软件开发平台,它既能运行在PC上做软PLC,也能作为内核嵌进很多硬件PLC里。对电气工程师来说,学会CODESYS V3.5,基本就等于摸到了一大类控制器和软件生态的共同入口。这篇笔记是我自己从零开始的实际学习记录,不照着官方手册念,专门讲哪些地方容易卡住、哪些坑我替你踩过,适合刚准备入门CODESYS、或者已经装好软件但不知道从哪下手的同行。

为什么说它值得学?你会发现,很多国产品牌的中型PLC,底层就是CODESYS V3.5内核,你前面学的东西,换个品牌IDE照样能用,这种“一次学习,多处复用”的性价比,在传统日系PLC上是体验不到的。

1. 先弄清楚CODESYS到底是个什么东西

1.1 它不是PLC,它是PLC的“操作系统”

CODESYS是德国3S公司开发的软件系统,全称是Controller Development System,现在最普及的是V3.5这个架构分支。你把它理解成PLC领域的“操作系统”,就比较准确了。它本身不生产硬件,而是提供一套完整的开发环境和运行时内核。工控机装上它的Runtime,就能变成一台软PLC;PLC厂商买下授权,把Runtime嵌进自家硬件,工程师就能用同一套CODESYS IDE去写程序。

V3.5这个版本号容易被新手误解成“软件版本3.5”,其实它指的是CODESYS V3这个内部的整体架构,V3.5是这套架构发展至今的版本线名称,后面还会继续迭代小版本,比如V3.5 SP19、SP20之类。你在官网下载开发环境时看到的是CODESYS V3.5 SP几,这个SP补丁包级别越高,功能越全,对老版本工程的兼容也做得不错。

CODESYS能干什么?一套环境里面你可以写PLC逻辑、做可视化HMI、配EtherCAT总线、组运动控制轴、开OPC UA服务器、接数据库、跑CIFX网关应用。对一般电气工程师来说,最常用的还是写逻辑、做仿真、完成设备数据采集,这三件事它都做得非常顺手。

它的开放性也是它火起来的关键原因。传统PLC厂商的编程环境往往只服务自家硬件,而CODESYS生态里有大量设备厂商的Target包:装上汇川的包就能连汇川PLC,装上某个国产小厂家的包就能连那个牌子的PLC,甚至同一个项目里你可以无缝换一个目标平台重新编译。

所以你在设备现场看到某个国产PLC,编程软件界面跟CODESYS长得一模一样时,不用惊讶,它不是“侵权仿冒”,而是真的内嵌了CODESYS Runtime。这个现象在近几年的国产PLC市场里非常普遍,也是我推荐电气工程师认真学它的最大理由。

1.2 为什么说电气工程师值得花时间学它

从个人职业角度看,学CODESYS能明显降低反复学习不同品牌软件的精力消耗。我见过不少工程师,会三菱又会西门子,工具书堆一柜子,但碰到一个新品牌还是得翻手册。CODESYS不一样,你只要把一套工具链磨熟,很多品牌PLC都能直接上手,比如汇川的中型机、信捷的部分系列、还有一些做控制器模块的设备商,本质都在CODESYS体系里。

再把视野拉高一点,TwinCAT 3,也就是倍福现在的核心开发环境,它底层同样是基于CODESYS V3架构演化出来的。虽然倍福做了大量改动和封装,界面也完全不一样了,但你如果已经具备CODESYS的任务调度、POU组织、符号配置这些基本概念,再去学TwinCAT,认知成本会低一个量级。

从技术属性上看,CODESYS比传统PLC开发更接近软件开发。它有完整的任务配置、库管理器、断点调试、在线修改、版本管理工具链。你写的不是一个“梯形图文件”,而是一个工程,一个可以复用、可以封装、可以通过库文件分发的软件项目。这种工作方式带来的效率提升,是长期写“单机逻辑”时感受不到的。

当然了,要说缺点也有。CODESYS自由度太高,要求工程师自己有一定的软件工程素养;任务和库的概念,对于只看过梯形图的老师傅来说,初期确实要适应一阵。但你只要把前面几章看完,自己动手跑通一个仿真工程,这层窗户纸很快就破了。

2. 环境搭建与第一个工程

2.1 下载、安装和许可证,半小时解决的几件事

先解决软件从哪来。CODESYS官方下载入口是它的官网和CODESYS Store,开发环境本体通常叫CODESYS Development System V3.5。官网下载需要注册账号,注册是免费的,下载也是免费的。我不建议去找什么绿化版、老版本绿色版,官方版本带自动更新,库文件全,出问题也好查。

安装时有几个小细节:

  • 安装路径别带中文,别放在有空格的长目录下,CODESYS的编译器在个别中文路径工程下会报一些莫名其妙的路径错误;
  • 安装完成后第一次启动,建议把整个安装目录和工程目录加入杀毒软件白名单,尤其是使用USB加密狗和仿真器的时候,杀毒软件误杀组件的情况我碰到过不止一次;
  • 如果你要用某个具体品牌的PLC,还需要去该品牌官网下载对应Target包和Device Description设备描述文件,装好后才能在设备类型列表里看到那个型号;
  • 只在电脑上学习,没有真机,就选CODESYS Control Win V3,这是官方提供的本机仿真运行时。

许可证是新手最容易懵的地方。CODESYS开发系统本体是免费可用的,本机仿真也默认能用。但某些专业组件、高级库、特定设备通讯协议,会提示需要许可证,常见的像CodeMeter授权、软授权文件、试用授权。遇到弹窗写“Licence required”,先别慌,多数情况是你用了一个需要专业授权的组件,切回最基本的工程和标准库,或者换成官方仿真器,就能正常跑。

我自己的习惯是:新建虚拟机专门用来装不同版本的CODESYS开发环境,避免多个SP版本互相覆盖导致库冲突。如果你只是验证性学习,一台Windows 10 64位电脑直接装也完全没有问题。

2.2 新建工程,用ST点亮第一个输出

第一次新建工程的操作流程,我建议严格按下面几步走,走通一次比看十遍教程都有效:

  1. 打开CODESYS,点击File -> New Project;
  2. 选择Standard Project,填写工程名称和保存路径;
  3. 选择设备:本机仿真选CODESYS Control Win V3;连真机则选对应的厂商PLC型号;
  4. 选择POU语言:推荐选ST(结构化文本),后面看别人的程序也最通用;
  5. 点击OK,系统会生成一个带PLC_PRG的默认工程结构。

在PLC_PRG里写第一段“点亮输出”的逻辑。这段程序的作用是把一个布尔输入直接复制给一个布尔输出,相当于传统PLC里的X0传到Y0:

VAR xStart : BOOL; xOutput : BOOL; END_VAR xOutput := xStart;

然后打开左侧设备树的PLC_PRG,在下方变量表里右键,把xStart和xOutput的属性改成“显示在IO映射中”,再把它们拖到设备树底部的IO映射界面去绑定具体的物理输入输出点。仿真环境下没有实际IO,可以直接在线写入变量看到效果。

点击菜单栏的“登录”按钮,首次登录会提示是否下载初始工程,点确认。注意,如果你没有提前启动仿真运行时,登录时它会提示找不到设备。解决办法是在设备树里双击CODESYS Control Win V3,在打开的设备列表中检查是否已经启动,或者右键设备节点选择“启动”选项。

下载完成后点击“运行”,程序就跑起来了。这时候你在在线监视模式下,右键变量xStart,选择“写入”,把它写为TRUE,会看到xOutput也跟着变为TRUE。这一步虽然简单,但它把工程创建、设备选择、变量映射、在线下载、运行监视这一整条链路全部打通了。

我强烈建议你在这个环节不要跳过任何一步,尤其是设备选择和在线下载,这是后续一切操作的地基。我第一次做时直接选错了设备类型,结果下载时一直提示设备不匹配,折腾了大半个小时才意识到是Target包的问题。

3. 理解任务、POU与库,才算真正入门

3.1 POU三兄弟:PRG、FB和FUN的区别

CODESYS里程序的基本组织单元叫POU,中文里通常叫程序组织单元。新手最容易混淆的是三类POU的用法:PRG、FB、FUN。

PRG,Program,它是程序入口,由任务决定什么时候被调用。一个工程里可以有多个PRG,只要在任务配置里把它挂上去,它就会被周期执行。PRG适合放顶层逻辑,比如整机的状态机、主流程调度。

FB,Function Block,功能块,它是带记忆、带内部状态的。同样一个FB被调用两次,两个实例的内部变量互不干扰。典型例子是TON定时器,你放两个TON实例,一个管启动延时,一个管停止延时,两个计时互不影响。自己写电机控制、温度PID、报警逻辑时,把它封装成FB是最合理的。

FUN,Function,函数,它是无状态的纯计算。输入相同,输出就一定相同,内部不能保存状态。比如数学函数ABS、SIN,就是我们说的FUN。自己写一个摄氏转华氏的换算函数,就很适合用FUN。

新手常见的错误是,想用一个FB,结果在声明区直接写TON(IN := ...),编译器立刻报错。正确做法是先实例化,再调用,比如:

VAR tonDelay : TON; END_VAR tonDelay(IN := xStart, PT := T#5S); IF tonDelay.Q THEN xOutput := TRUE; END_IF

这就是FB和FUN最直观的区别:FB要先new一个实例,FUN直接调就可以。把这个关系理顺,后面看系统库、看别人封装的功能块,都会轻松不少。

3.2 任务配置:控制周期和优先级不是随便填的

CODESYS里的任务Task,是定时调度POU执行的机制。默认工程会生成一个MainTask,周期通常是20ms或50ms。你把POU挂到任务上,它就会按你设定的周期被循环调用。

任务配置里有两个东西我建议新手一开始就养成好习惯:周期和优先级。

周期决定了程序多久执行一次。做布尔逻辑、模拟量处理、普通设备控制,20ms到50ms完全够用;做运动控制轴循环,一般放到1ms到4ms;HMI刷新、数据采集上传,放到100ms到500ms也合理。不要所有程序都塞到同一个快速任务里,那样只会白白消耗CPU。

优先级决定任务之间的抢占关系。CODESYS中优先级数字越小,优先级越高,高优先级任务会打断低优先级任务。设计多任务时,要特别注意全局变量在不同任务之间并行访问的问题。一个低优先级任务正在读取某个全局数组,高优先级任务突然修改了这个数组,会导致数据不一致。解决思路无非两种:用系统自带临界区保护机制,或者把交互变量在任务开头复制到本地缓存。

仿真器里跑多任务,并不一定能真实反映硬件的时序表现。仿真环境的定时精度依赖于操作系统调度,真的用PLC运行时,任务周期才是硬件级的。这点在后期做运动控制时尤其明显,仿真器里看不出来的抖动,真机上可能直接造成轴抖动报警。

3.3 自己封装库文件:从面对代码到面向接口

学会把常用的POU打包成库文件,是CODESYS进阶的一道分水岭。我第一次听到“CODESYS如何生成库文件”这个问题时也愣了一下,后来实际操作一遍之后发现,过程其实非常直接。

先说一下思路:传统PLC里,你想在一个新项目里复用旧项目的程序,最常见的办法是复制粘贴,但粘贴多了很容易带进来一堆无关变量和隐式依赖。CODESYS的库机制解决的就是这个问题,它让你把功能块、函数、全局变量统一编译成库文件,别人或新项目只需要安装这一个文件,就能使用里面全部功能。

操作步骤大概是这样的:

  1. 在CODESYS开发环境中新建一个Library工程,也就是库工程;
  2. 在库工程里添加需要复用的POU,比如一个电机控制FB、一个CRC校验函数、一个报警分组处理FB;
  3. 填写项目信息,包括库名称、版本号、公司名、作者;
  4. 编译库工程,编译完成后在输出路径下生成一个.library文件;
  5. 打开目标应用工程,在库管理器左下角点击“安装库”,选择刚才生成的.library;
  6. 安装完成后,库管理器里就能看到这个库,程序里也能直接调用了。

这里面有几个容易踩的点。库工程里不要写死具体硬件的IO地址,否则库移植到别的设备上就废了,要把IO访问放到调用侧,通过变量传递进去。库内部POU命名要加前缀,比如厂家名或项目名,否则很容易和系统库里的同名功能块冲突。每次修改库源码后,记得提升版本号再编译,不然目标工程里的库不会自动更新。

封装库的收益是长期的。我自己把常见的设备控制逻辑、数据校验算法、故障报警处理沉淀成标准库之后,新项目开发周期明显缩短,而且调试阶段碰到的问题也少了,因为库内部逻辑已经经过多轮项目验证。

4. 数据采集与对外通信:符号配置、plc-recorder与数据库

4.1 符号配置:外部程序读不到变量时的第一查看点

很多人在把CODESYS的数据对外共享时,都会卡在一个莫名其妙的问题上:程序编译正常、在线监视正常、上位机就是读不到变量。这里绕不开的就是符号配置。

CODESYS的工程变量默认对外部工具是不可见的,你必须手动告诉它:“这个变量可以对外开放,允许外部程序通过某种协议读取”。路径很简单,在Application节点上右键,找到“符号配置”选项。打开配置窗口后,你可以勾选需要导出的POU和全局变量,同时选择对外服务方式,比如OPC UA、Modbus TCP、自定义符号文件等。

只勾选还不够。我实战中碰到过的坑是:勾选完成了,上位机依然读不到。排查半天发现,设备树里Application节点下的“下载选项”没有勾选“下载时生成符号文件”,或者生成的符号文件路径和外部工具读取的路径不一致,导致对方拿到一个旧文件。

还有一点容易被忽略,单个变量的“可见性”属性也会影响符号导出。右键某个变量,在属性页里可以看到它的访问修饰符,如果设成了“私有”或“仅内部”,即使你去符号配置里勾了POU,外部工具也可能看不到它。想对外读,就把变量属性设为“调用者可见”、“对外可见”这一类。

我的建议是:任何需要上位机、数据库、第三方向量软件采集的数据,都尽量集中在几个专门做通信的全局变量结构体里,而不是满工程零散布变量。这样符号配置看一眼就清楚,外部工具配置也简单,排查问题也快。

4.2 plc-recorder采集CODESYS变量的前提与实战

PLC-Recorder是目前不少同行在做数据记录时喜欢用的第三方采集软件,它可以把PLC变量按设定周期采集下来,写入CSV或数据库,配置比传统组态软件轻很多。很多人刚拿到手时不知道怎么和CODESYS对接,其实核心就一句话:让plc-recorder拿到CODESYS的符号文件。

第一步,在CODESYS的符号配置里,把变量对应的XML符号文件导出或生成出来。不同版本的CODESYS导出格式可能略有差异,但我建议优先用“用户自定义XML”这个选项,兼容性更好。

第二步,在plc-recorder的配置界面里添加一个新的连接,数据源选择对应的CODESYS驱动。如果你不确定用哪个,先试“CODESYS XAE/符号访问”这一类,指向刚才导出的XML文件。软件会自动读取变量列表,你在里面勾选要采集的变量,设置采集周期即可。

第三步,把配置下载到CODESYS PLC,确保符号配置生效。注意,如果PLC处于停止状态,或者工程没有被在线下载,符号文件就不会被运行时加载,plc-recorder那边自然是一片空白。

躬身入局之后你会发现,plc-recorder看不到变量时,90%的问题都在符号文件没生成、路径不对、工程没重新激活这三点上。剩下的10%才是通讯协议问题,比如PLC的OPC UA服务没启动、防火墙拦了端口。

还要啰嗦一句:第三方采集软件的采样周期不要小于PLC任务周期,否则采集到的是重复数据,没有任何意义,还白白占用网络资源。一般设置成PLC任务周期的3到5倍以上比较合理。

4.3 数据库对接:官方库、第三方MySQL库与实时约束

关于CODESYS连数据库,很多人的第一反应是“PLC还需要连数据库?”但等你做设备联网改造、做工艺数据追溯的时候就知道,这个需求有多普遍。CODESYS本身并不是完全没有数据库能力,只是官方默认不随开发环境一起安装数据库类库,需要自己去库商店里找,或者用第三方的库。

先说明一条我在项目中反复撞墙后总结出的铁律:绝不要把数据库操作放进高优先级控制任务里。数据库连接、INSERT、查询,尤其是首次建立连接,耗时可能达到几百毫秒甚至更久,你把这段代码放进10ms的任务里,一个周期没执行完,下个周期又来了,轻则程序卡顿,重则看门狗超时直接停机。正确做法是把数据库操作放到一个独立的慢速任务里,周期500ms到1s,数据先用环形队列缓存,再批量入库。

数据库相关的库有几种常见选择。第一种是官方提供的数据库类库,走ODBC桥接,先配置ODBC数据源,再在CODESYS里调用库函数。这种方法的好处是兼容多个数据库,坏处是ODBC依赖Windows环境,在纯Linux Runtime上不好使。第二种是社区/第三方写的MySQL原生协议库,比如网上搜“alongwu”能找到作者发布的一个MySQL第三方库(CODESYS),它直接封装了MySQL通信协议,不依赖ODBC,典型用法是先建立连接对象,再执行SQL。

下面给出一个沿类似思路的MySQL第三方库的调用示意,库API不同,但基本调用模式差别不大:

VAR dbConn : MYSQL.Connection; dbQuery : MYSQL.Query; iAffect : DINT; END_VAR dbConn.Host := '192.168.1.20'; dbConn.Port := 3306; dbConn.User := 'plc'; dbConn.Password := 'password'; dbConn.Database := 'factory'; dbConn.Open(); IF dbConn.IsConnected() THEN dbQuery.Connection := dbConn; dbQuery.Sql := 'INSERT INTO run_log(ts, val) VALUES(NOW(), 123)'; dbQuery.Execute(); iAffect := dbQuery.AffectedRows(); END_IF

这段逻辑不复杂,但结合我的实际经验必须提醒几个点。程序里字符串变量如果带单引号,直接拼到SQL里会把语句截断,INSERT报错,所以涉及字符串入库前一定要做转义和长度限制。数据量大的时候不建议每周期单条INSERT,而应该攒一批后批量插入,明显更快,也减少对PLC的影响。

第三方库的授权问题也要自己确认,个人学习和验证通常没问题,但商用项目里最好明确授权条款,或者换用官方库、用中间的采集软件转发,避免给自己挖坑。

4.4 汇川等国产PLC的CODESYS生态

聊到CODESYS生态,就绕不开汇川。汇川的中型PLC,比如AM系列,编程软件InoProShop底层就是CODESYS V3.5内核。你提前在通用CODESYS里练熟的手感,到了InoProShop里会发现大部分能直接迁移。这也是我前面强烈建议学CODESYS的原因之一,它确实能减少你在多个品牌软件之间的重复学习。

不过,通用CODESYS和厂商定制IDE之间,还是有一些差异要心里有数。厂商会在内核上套一层自己的设备配置界面,IO映射、总线配置的位置可能不同;运动控制库、视觉库也常常被厂商二次封装成自己的命名空间;厂商自带的库可能跟通用库不兼容,乱装通用库会有风险。

最稳妥的学习路径是:先在通用CODESYS环境里把ST编程、任务配置、库管理、符号配置这些软技能打牢,再下载对应厂商的免费编程软件连真机调试。这样两边不会打架,也更容易看出厂商到底改了哪些东西。

我用汇川设备时还碰到一个习惯问题:它们有些型号不支持在线修改,即使只改一个变量类型,也可能要求全量停机下载。如果你习惯了西门子那种在线修改的灵活性,刚开始会很不适应。这也提醒我们,前期把通信变量和地址规划详细了,能少很多停机修改的麻烦。

5. 调试与排查:这些坑我替你踩过了

5.1 下载后PLC不运行的原因

第一次登录CODESYS控制器的流程是:点击登录 -> 提示下载工程 -> 确认下载 -> 再点运行。很多新手习惯点完登录就直接运行,结果提示“没有活动应用”,这其实是因为还没下载。CODESYS里下载和运行是两个独立动作,下载是把编译后的应用装载进设备,运行是让控制器开始执行。

还有一个容易忽略的是Boot Project,也就是开机自启工程。你下载完工程后,如果不上传到Boot区,控制器断电重启后就不会自动运行程序,设备现场断电再来电,机器傻在那里,非常尴尬。在Application节点上右键,把当前应用设置为Boot Application,能解决这个问题。

在线修改是另一个常见坑。CODESYS支持在线修改,但如果你改了变量声明结构,加了新POU,或者改了任务配置,在线修改就会失败,提示需要停机下载。这时候别硬点,老老实实全量下载。特别是生产设备在跑,你手一抖就停机了,操作前一定跟现场确认状态。

5.2 仿真正常、真机报错的排查顺序

仿真跑得好好的,一上真机就各种幺蛾子,这是CODESYS新手必经阶段。排查的时候别慌,按顺序来。

先看设备树有没有红色报错节点。PLC日志里一般会给出错误码和错误描述,比如EtherCAT从站丢失、某个IO模块故障。设备树的报错是最直接的入口,比你去猜程序快得多。

再看总线IO状态。EtherCAT主站扫描不到从站,先查从站是否上电、站号是否冲突、网线是否接好、终端电阻是否匹配。模拟量模块读数异常,看一下模块配置的通道类型、量程、溢出位是不是被置位了。

然后才轮到程序。用在线监视看输入条件、输出状态、中间变量,重点排查是不是仿真里依赖了本机时间、文件路径等环境相关的东西。

最后还有一些玄学问题:真机跑得比仿真快或慢导致时序差异。仿真用的是Windows线程调度,跟硬件的确定性执行不一样,你在仿真里把时间参数调得刚好,真机可能偏差很大,需要用Trace记录实际周期来校准。

5.3 在线调试三板斧:监视、强制、Trace

CODESYS的在线调试工具,用熟之后比传统PLC方便不少。

监视窗口是默认的在线表格,程序运行时你可以看到所有变量实时值,还可以自己添加感兴趣变量到Watch窗口,集中观察。

强制和写入的区别要分清。强制Force会持续把变量锁定在某个值,哪怕程序运行到下一周期,它也一直被“钉”在那个值上,适合模拟输入信号、测试分支逻辑。写入Write是一次性写值,下个周期程序正常执行,适合手动修改参数、给某个输出临时置位。调试完一定记得取消所有强制,不然可能导致现场事故,我见过有人强制了一个安全信号忘记取消,设备差点出事。

Trace功能相当于PLC内部的简易示波器。你可以在线配置要记录的环境变量、采样周期、触发条件,让它记录一段数据,用来分析变量变化曲线。调PID、看轴速度波动、查通信数据抖动,都很依赖Trace,建议早点熟悉。

个人的小习惯是:在程序里预留一个重要中间变量区,调试时把它们全部暴露到符号配置里,这样既方便Trace,也方便上位机诊断数据,一举两得。

5.4 常见问题速查表

下面这个表是我在实际学习和项目中经常用到的排障清单,一条条对着查能省很多时间。

问题现象常见原因解决思路
下载提示“工程与设备不匹配”Target包版本或设备描述不对更换匹配的设备描述文件,升级Target包
仿真登录失败,找不到设备未启动CODESYS Control Win运行时在设备树双击设备,启动对应仿真运行时
外部工具读不到变量符号配置未导出、未激活,或变量可见性不对重新生成符号并在线下载,检查变量属性
程序编译正常但不执行POU没挂到任务上,或任务周期为0在任务配置里添加POU,设置合理周期
在线修改时报错要求停机变量声明或任务结构发生变化改为全量下载,提前规划好变量结构
数据库写入导致程序卡顿数据库操作放进了控制任务单独慢速任务+队列缓存,批量入库
库安装后找不到功能块库版本与当前工程不兼容检查库依赖,重新安装匹配版本库
变量值变化但组态软件不刷新符号没有重新发布、服务未启动重新下载激活工程,启动对应通信服务

这个表不是标准答案,每个项目环境不同,照样会有各种不适用的情况,但排查顺序本身值得参考:先设备、再总线、再程序、再通信。

最后说两点个人体会

CODESYS这套系统,真正拉开使用效率差距的,不是指令记了多少,而是任务怎么组织、库怎么封装、数据怎么对外流动。我自己的习惯是拿到一个新控制器,先花半天走一遍环境搭建、点灯、仿真、符号导出这个完整链路,再开始写业务逻辑。这套基础链路熟练之后,后面接触什么内核是CODESYS的设备都会很从容。

再分享一个细节:保持官方开发环境的版本尽量新,但工程文件尽量在主力稳定版本上维护。CODESYS的旧工程被新版本打开后,如果不是需要用到新功能,不要轻易乱升级组件和库,避免整个工程被“顺便”改到无法回退的版本状态。装一个虚拟机专门做版本兼容性验证,是我这几年的常规操作了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询