新闻详情

新闻详情

首页 / 资讯中心 / 详情

LabVIEW UDS刷写主VI设计:状态机+事件+队列架构

发布时间:2026/9/13 19:48:49来源:尧图网络
LabVIEW UDS刷写主VI设计:状态机+事件+队列架构
1. 项目概述这不是一个“普通”的Main.vi而是一套CAN UDS刷写流程的神经中枢你打开LabVIEW新建一个VI拖几个控件、连几根线点运行——这叫“Hello World”。但当你看到标题里写着“基于图莫斯的CAN UDS升级上位机-LabVIEW版本十二Main.vi — 主VI与刷写流程编排”你就该明白眼前这个Main.vi已经不是入门练习而是整套UDS刷写系统真正意义上的“大脑”和“指挥室”。它不处理单帧CAN报文的字节序转换也不负责解析0x7F响应里的NRC码含义但它必须知道什么时候该发0x10 03进扩展会话什么时候该等0x7F 10 78再发0x27 01请求种子什么时候该把加密后的密钥塞进0x27 02又在哪个毫秒级窗口里监听0x67 02的成功响应。图莫斯Toumos作为底层CAN通信引擎只管收发原始帧而Main.vi就是那个把冷冰冰的UDS服务调用、状态跳转、超时重试、错误恢复、用户交互全部编织成一张可执行、可调试、可复现的实时控制网的人。它解决的核心问题是把ISO 14229-1标准里定义的抽象服务序列落地为一台工控机上能稳定跑通ECU固件升级的完整闭环。适合谁不是刚学完LabVIEW基础控件的新手而是已经用过图莫斯API、写过单个UDS服务调用VI、正卡在“流程串不起来”“状态机总崩”“超时逻辑乱套”这些真实痛点上的汽车电子工程师、诊断开发人员或者正在做毕业设计/产线刷写工具的自动化专业学生。关键词“图莫斯”“CAN”“UDS”“LabVIEW”“Main.vi”不是并列关系而是层级依赖图莫斯提供CAN通道能力CAN承载UDS协议UDS定义服务逻辑LabVIEW构建可视化与控制流而Main.vi就是把这四层能力拧成一股绳的那个关键螺栓。2. 整体架构与设计思路为什么必须用状态机事件结构队列而不是“顺序执行”2.1 拒绝“从上到下一条线”的致命诱惑很多初学者拿到UDS刷写需求第一反应是画个流程图发0x10→等0x50→发0x27 01→等0x67 01→算密钥→发0x27 02→等0x67 02……然后在LabVIEW里用Sequence Structure顺序结构一气呵成。我试过也踩过坑。结果是什么一次通信延迟整个VI卡死一个NRC 0x78请求正确但需稍后重试程序直接报错退出用户想中途点“暂停”或“中止”发现按钮根本没响应——因为CPU全被顺序结构占着连前面板刷新都卡顿。这不是LabVIEW的问题是设计范式错了。UDS刷写本质是异步、事件驱动、强状态依赖的过程。ECU不会按你的节奏出牌它可能在任意时刻发来0x7F响应也可能在安全访问成功后突然掉线。把这种不确定性硬塞进同步顺序流里等于给系统埋了定时炸弹。2.2 状态机State Machine给刷写流程装上“交通信号灯”所以Main.vi的核心骨架必须是经典的状态机。我们定义一套清晰、互斥、覆盖全场景的状态枚举Idle空闲等待用户点击“开始刷写”。EnterSession进入会话发送0x10 03等待0x50 03。SecurityAccessStep1安全访问第一步发送0x27 01等待0x67 01。SecurityAccessStep2安全访问第二步发送0x27 02含密钥等待0x67 02。DownloadRoutine下载例程发送0x34请求下载、0x36传输数据块、0x37退出传输循环直到所有块完成。TransferExit退出传输发送0x37确认ECU已准备好接收新应用。Programming编程发送0x31 01擦除Flash、0x31 02校验、0x31 03编程等服务。VerifyAndExit校验与退出发送0x31 03校验、0x11 01复位结束流程。ErrorHandling错误处理捕获NRC、超时、CAN总线错误等决定是重试、降级还是终止。UserAbort用户中止响应“停止”按钮执行安全退出序列如发0x11 01复位。每个状态只做一件事发一帧或多帧请求设置超时计时器然后立刻退出把控制权交还给状态机主循环。这样CPU永远有余力处理UI事件、日志记录、甚至后台监控。状态切换不是靠“下一步”连线而是靠一个全局的“Next State”变量由当前状态的执行结果成功/失败/NRC动态决定。比如在SecurityAccessStep1状态如果收到0x67 01就设Next State SecurityAccessStep2如果收到0x7F 27 35请求超出范围就设Next State ErrorHandling。这种解耦让逻辑异常清晰调试时一眼就能看出“卡在哪一步”。2.3 事件结构Event Structure让UI和通信“各干各的”状态机管流程事件结构管交互。Main.vi的顶层循环里必须嵌套一个事件结构专门监听两类事件UI事件如“Start Button.Value Changed”、“Stop Button.Value Changed”、“Config File Path.Changed”。当用户点“开始”事件结构捕获到就触发状态机从Idle跳到EnterSession点“停止”则强制切入UserAbort状态。关键是这些事件响应代码必须极短——只改状态变量不执行任何耗时操作比如不在这儿直接发CAN帧。否则UI线程被阻塞按钮就“失灵”了。通信事件这是图莫斯的精髓。图莫斯API通常提供“帧接收回调”或“事件通知”机制如Toumos_RegisterFrameCallback。我们在Main.vi初始化时把这个回调注册到一个LabVIEW的“用户事件”User Event上。每当图莫斯底层收到一帧CAN报文它就触发这个用户事件把原始帧数据ID、DLC、Data[]打包发给LabVIEW。事件结构捕获到这个“CAN Frame Received”事件后立即把帧数据入队到一个FIFO队列见2.4然后退出。整个过程毫秒级完成绝不阻塞。提示LabVIEW的“用户事件”是跨线程安全的比用全局变量或局部变量传递数据可靠得多。千万别用“属性节点”去轮询按钮状态那是性能杀手。2.4 队列Queue在通信线程和UI线程之间建一座“缓冲桥”状态机要处理ECU响应事件结构要接收CAN帧但这两者运行在不同线程LabVIEW默认UI线程和后台循环线程分离。直接共享数据会引发竞态条件。解决方案是生产者-消费者模式图莫斯的回调是“生产者”它把收到的每一帧CAN数据构造成一个簇timestamp,can_id,dlc,data[8]塞进一个预定义的队列状态机主循环是“消费者”它在每个状态执行前先从队列里“尝一口”Peek Queue看有没有新帧如果有就“取出来”Dequeue Element解析没有就继续走自己的状态逻辑。队列大小设为100足够既防丢帧又不占过多内存。这个队列就是连接底层硬件和上层逻辑的唯一、安全、高效的“数据管道”。没有它你的Main.vi要么丢帧要么卡死要么出现难以复现的随机错误。3. 核心细节解析与实操要点从状态机到UDS服务调用的落地密码3.1 状态机主循环如何避免“忙等”和“假死”一个健壮的状态机主循环绝不能是“While Loop 延时10ms”的简单组合。它必须包含三个关键组件状态选择结构Case Structure输入是当前状态枚举输出是下一个状态枚举和本状态的执行代码。超时管理器Timeout Manager每个状态启动时记录当前时间戳Get Date/Time in Seconds并设定一个最大等待时间如EnterSession状态设为2000ms。在状态执行代码末尾计算Current Time - Start Time如果超时强制设Next State ErrorHandling并记录NRC 0x78请求正确但需稍后重试——这是UDS标准里最常被忽略的“软超时”处理。事件结构Event Structure放在While循环内但必须设置“Timeout”端口如50ms。这意味着循环每50ms至少检查一次是否有UI或CAN事件发生。如果没有事件就执行一次状态逻辑如检查超时如果有事件就优先处理事件如更新状态、入队CAN帧。这个50ms的Timeout值是经验值太小如1msCPU空转耗电太大如500msUI响应迟钝。实测下来50ms是兼顾流畅度和实时性的黄金值。注意LabVIEW的“Wait Until Next ms Multiple”函数不能用在这里。它会让循环严格对齐毫秒边界一旦某个状态执行耗时超过设定值如设了100ms但实际用了150ms下一次循环就会“跳过”一个周期导致事件响应延迟。用带Timeout的事件结构才是正解。3.2 UDS服务调用VI封装不是为了炫技而是为了可维护性Main.vi里绝不应该出现“写CAN帧ID0x7DF, DLC8, Data[0x02,0x10,0x03,0x00,0x00,0x00,0x00,0x00]”这样的硬编码。所有UDS服务调用必须封装成独立的子VI例如UDS_EnterSession.vi、UDS_SecurityAccess_Step1.vi。这些子VI的接口非常干净输入Session TypeU8如0x03表示扩展会话、CAN Channel图莫斯句柄、Timeout msU32。输出Success?布尔、Response DataU8数组、NRCU80表示无错误、Error String字符串。封装的好处立竿见影第一Main.vi主循环里状态逻辑变得极其清爽比如EnterSession状态只需调用UDS_EnterSession.vi然后根据Success?输出决定下一步完全不用关心CAN帧怎么组、怎么发、怎么收。第二当ECU厂商变更了会话进入方式比如要求先发0x10 02再发0x10 03你只需要修改UDS_EnterSession.vi内部Main.vi一行代码都不用动。第三这些子VI可以被其他项目复用比如诊断仪的读取DTC功能也能调用同一个UDS_EnterSession.vi。3.3 CAN帧组包与解析大端小端、填充字节、ID映射的实战陷阱图莫斯的CAN帧结构通常是一个簇Cluster包含IDU32、DLCU8、DataU8数组长度8。但UDS协议规定所有多字节数据如服务ID、子功能、地址、长度必须按大端序Big-Endian传输。而x86 CPU是小端序Little-Endian。这就意味着当你想发一个32位地址0x12345678不能直接Build Array成[0x12,0x34,0x56,0x78]——LabVIEW的Number To Byte Array默认是小端会生成[0x78,0x56,0x34,0x12]ECU直接拒收。正确做法是先用Number To Byte Array转成小端数组再用Reverse 1D Array反转得到大端数组。同理解析ECU返回的32位长度字段也要先Byte Array To Number小端再Reverse最后To Number。另一个坑是填充字节Padding。UDS规定0x34请求下载服务的请求帧数据域格式是[Subfunction][Address Length][Memory Address][Length Format][Length]。其中Address Length和Length Format都是U8但Memory Address和Length的字节数由前者决定。比如Address Length4Length Format4那整个数据域就是[0x00][0x04][0x12,0x34,0x56,0x78][0x04][0x00,0x00,0x01,0x00]共11字节。但CAN帧DLC最大是8怎么办答案是分帧传输。0x34只传地址和长度信息0x36传输数据才传真正的二进制数据块。很多新手试图把11字节硬塞进一帧结果ECU返回NRC 0x13不正确的消息长度。记住UDS不是TCP没有自动分片一切分帧逻辑必须由上位机自己实现。最后是CAN ID映射。图莫斯通常支持标准帧11位ID和扩展帧29位ID。UDS默认使用标准帧请求ID0x7DF响应ID0x7E8ECU 1到0x7EFECU 8。但有些车厂用扩展帧ID0x18DB33F1请求和0x18DAF133响应。Main.vi必须有一个配置项让用户选择ID类型并在组包时自动适配。硬编码0x7DF会让你的上位机在产线上寸步难行。4. 实操过程与核心环节实现从零搭建Main.vi的完整步骤与参数详解4.1 初始化阶段图莫斯加载、CAN通道配置、UI准备Main.vi的初始化Initialization不是可有可无的“热身”而是整个流程稳定性的基石。它必须在While循环开始前一次性完成以下三件事第一步加载图莫斯DLL并获取句柄# 在LabVIEW中使用Call Library Function Node (CLFN) # 路径指向图莫斯提供的toumos.dll如C:\Toumos\toumos.dll # 函数名Toumos_Init # 参数无 # 返回值U32图莫斯句柄后续所有API调用都需此句柄关键点Toumos_Init必须成功返回非零句柄否则后续所有CAN操作都会失败。我在某次调试中发现句柄为0查了半小时最后发现是toumos.dll版本和LabVIEW 2020 64位不兼容换用32位LabVIEW才解决。所以初始化后务必加一个Error Check如果句柄0弹出对话框“图莫斯初始化失败请检查DLL路径和位数匹配”并禁用所有按钮。第二步配置CAN通道# 调用 Toumos_OpenChannel # 参数句柄、通道号0或1、波特率如500000、工作模式Normal/Loopback # 返回值U32通道句柄这里有个隐藏参数采样点Sample Point。CAN协议规定一个位时间分为同步段、传播段、相位缓冲段1和2。采样点是相位缓冲段1的结束位置理想值是87.5%。图莫斯API可能不直接暴露这个参数但它的波特率设置函数内部会根据你选的波特率如500k自动计算。如果你的ECU通信不稳定不要急着换线先在图莫斯文档里查查是否支持手动设置采样点。我遇到过一次500k波特率下采样点只有75%导致误码率飙升把采样点调到87.5%后通信瞬间稳定。第三步UI元素初始化与事件注册设置“开始”按钮为EnabledTrue“停止”按钮为EnabledFalse初始不可点。清空日志显示控件如Log Text Box。最关键的一步调用Toumos_RegisterFrameCallback把图莫斯的帧接收回调绑定到LabVIEW的“用户事件”上。这个回调函数在C/C里写但LabVIEW里只需配置好事件引用Event Refnum图莫斯收到帧就自动触发。别忘了在While循环结束时调用Toumos_UnregisterFrameCallback清理资源否则程序退出时可能崩溃。4.2 刷写流程编排以“安全访问”为例的深度拆解我们以SecurityAccessStep1安全访问第一步这个最典型、最容易出错的状态为例展示Main.vi如何精确控制。状态进入逻辑当前状态为EnterSession且收到0x50 03响应后设Next State SecurityAccessStep1。同时初始化一个Seed变量U32初始值为0初始化一个Retry CountU16初始值为0。状态执行逻辑在Case Structure的SecurityAccessStep1分支内发请求调用UDS_SecurityAccess_Step1.vi输入Subfunction0x01CAN Channel句柄。该子VI内部组包[0x02,0x27,0x01]DLC3ID0x7DF通过图莫斯Toumos_WriteFrame发出。设超时记录Start Time Get Date/Time in SecondsTimeout 2000ms。收响应进入一个子循环While Loop每次迭代从CAN帧队列Dequeue Element如果队列为空Wait (ms) 10然后检查Current Time - Start Time Timeout超时则跳出循环设Next State ErrorHandlingNRC 0x78。如果收到帧检查ID 0x7E8假设ECU地址为0x08且Data[0] 0x066字节响应Data[1] 0x67服务IDData[2] 0x01子功能则提取Data[3..6]作为4字节种子Seed Byte Array To Number([Data[3],Data[4],Data[5],Data[6]])注意大端设Next State SecurityAccessStep2跳出循环。如果收到ID 0x7E8但Data[0] 0x03且Data[1] 0x7F则NRC Data[3]。常见NRC0x35请求超出范围可能是ECU不支持0x27、0x7F服务不支持、0x24请求序列错误说明会话没进对。此时根据NRC决定0x35和0x7F直接进ErrorHandling0x24则先回退到EnterSession重试。状态退出逻辑无论成功或失败都要把Seed值存入一个全局变量或通过移位寄存器传递供SecurityAccessStep2状态使用。Retry Count在每次失败后自增如果Retry Count 3则强制终止避免无限重试。这个看似简单的“发种子请求”背后是时间、状态、错误、重试的精密协同。Main.vi的价值正在于把这种复杂性封装成一个可预测、可调试、可复现的原子操作。4.3 错误处理与用户交互让“失败”也变得优雅UDS刷写失败是常态不是例外。Main.vi的ErrorHandling状态不是流程的终点而是用户体验的起点。它必须做到三点可读、可控、可溯。可读日志显示不能只写“刷写失败”。要精确到时间戳精确到毫秒当前状态如SecurityAccessStep1收到的原始CAN帧ID0x7E8, Data[0x03,0x7F,0x27,0x35]解析出的NRC含义NRC 0x35: “Request Out Of Range”建议操作“请检查ECU是否支持安全访问服务或确认会话模式是否正确”可控提供三个按钮“重试当前步骤”、“返回上一步”、“终止并复位”。点击“重试”就重新执行当前状态逻辑点击“返回上一步”就把Next State设为前一个状态如从SecurityAccessStep1返回EnterSession点击“终止并复位”则调用UDS_Reset.vi发0x11 01然后设Next State Idle。这三个按钮把主动权交还给工程师而不是让程序自作主张。可溯所有关键事件状态切换、CAN帧收发、错误发生都写入一个CSV文件包含时间、状态、ID、DLC、Data、NRC。这个日志是售后分析问题的唯一依据。我曾用它定位过一个产线问题日志显示所有失败都发生在DownloadRoutine状态且ECU返回的NRC总是0x31请求范围错误。对比正常日志发现是二进制文件的起始地址配置错了差了0x1000。没有这个详细日志排查可能要花一周。5. 常见问题与排查技巧实录那些官方文档不会告诉你的“血泪经验”5.1 “CAN not open com port”类错误图莫斯的“端口”不是COM口网络热词里高频出现can not open com port但这对图莫斯是伪命题。图莫斯是直接操作CAN控制器芯片如MCP2515、SJA1000或USB-CAN适配器如PCAN-USB、Kvaser Leaf的驱动它根本不经过Windows的COM口抽象层。当你看到这个错误99%的情况是驱动未安装去图莫斯官网下载对应硬件的驱动不是Windows自带的“USB Serial Device”。比如Kvaser设备必须装Kvaser Drivers。权限不足在Windows 10/11上LabVIEW有时需要“以管理员身份运行”才能访问底层硬件。右键LabVIEW图标选“以管理员身份运行”再试。硬件冲突同一台电脑插了多个USB-CAN图莫斯可能认错设备。用图莫斯自带的DeviceList工具或调用Toumos_GetDeviceCountAPI确认识别到几个设备再指定正确的Device Index。实操心得在Main.vi初始化时加一个“硬件自检”步骤。调用Toumos_GetDeviceCount如果返回0直接报错返回0再调用Toumos_GetDeviceInfo获取设备型号和序列号显示在UI上。这样用户一眼就知道“设备插没插好”。5.2 UDS NRC 0x78Request Correctly Received - Response Pending的“幽灵超时”这是UDS刷写中最让人抓狂的NRC。ECU明明收到了你的请求如0x27 01也告诉你“稍等”但就是不发0x67 01。原因通常是ECU内部在做耗时操作比如读取OTP区域、计算HMAC。官方文档说“等待时间由ECU决定”但没说最长等多久。我的经验是保守策略所有状态的超时时间必须大于ECU手册里写的“最大响应时间”。比如手册写“安全访问种子响应最大2秒”你的SecurityAccessStep1超时就得设成3000ms。激进策略在ErrorHandling状态如果捕获到NRC 0x78不直接报错而是启动一个“二次等待”子循环再等1000ms期间持续收帧。如果这1000ms内收到0x67就当成功如果没收到再报错。这个“二次等待”解决了80%的NRC 0x78误判。5.3 LabVIEW安装错误与Runtime Engine冲突一个被低估的环境杀手热词里labview安装错误、labview runtime engine2016下载频繁出现这背后是LabVIEW版本生态的残酷现实。图莫斯的DLL通常只针对特定LabVIEW版本编译如LV 2018 64-bit。如果你用LV 2020打开即使能编译运行时也会因ABIApplication Binary Interface不兼容而崩溃。解决方案只有两个严格匹配图莫斯SDK文档里明确写了支持的LabVIEW版本你就必须用那个版本。别想着“差不多就行”。Runtime隔离如果产线电脑只能装Runtime Engine那就必须用图莫斯提供的“Runtime版本DLL”而不是开发版DLL。开发版DLL依赖LabVIEW的完整开发环境Runtime Engine里没有。血泪教训我曾为一个客户部署用LV 2019编译的EXE在客户装了LV 2018 Runtime的电脑上运行一切正常。但客户后来升级了Runtime到2020EXE就闪退。查了半天发现是图莫斯DLL里的一个全局变量初始化方式在2020 Runtime里被优化掉了。最终方案是为客户打包一个“绿色版”里面包含LV 2018 Runtime和图莫斯DLL彻底隔离系统环境。5.4 CAN总线仲裁与“Can bus off”物理层的终极审判当刷写进行到一半突然所有CAN帧收不到图莫斯API返回CAN Bus Off错误恭喜你触达了CAN总线的物理极限。原因无非两个终端电阻缺失标准CAN总线两端必须各接一个120Ω电阻。少一个信号反射严重误码率飙升。用万用表量一下总线CAN_H和CAN_L之间的电阻应该是60Ω两个120Ω并联。不是赶紧补。线缆过长或干扰CAN标准最大长度是40米1Mbps。如果你的线缆绕了车间一圈长达100米那再好的上位机也救不了。换成CAN FD或加中继器是硬件方案软件上唯一能做的是捕获Bus Off错误后调用Toumos_ResetController重启CAN控制器并提示用户“请检查物理连接”。这些问题没有一个能在LabVIEW代码里“修复”。Main.vi的职责是第一时间感知、准确上报、安全退出。把物理世界的约束转化为软件世界的明确反馈这才是专业上位机的担当。我在实际项目中发现一个设计良好的Main.vi其价值远不止于“让刷写跑通”。它像一面镜子照出ECU固件的健壮性NRC分布、产线环境的稳定性Bus Off频率、甚至测试工程师的操作习惯重试次数统计。每一次点击“开始”都是对整个汽车电子研发链条的一次压力测试。所以别把它当成一个简单的流程控制器它是连接代码世界与钢铁世界的最后一道精密阀门。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SpringBoot线上教学平台开发实战与架构解析 2026/9/13 20:33:55

SpringBoot线上教学平台开发实战与架构解析

1. 项目概述:新工科线上教学辅助平台的设计初衷作为一名经历过多次毕业设计指导的老手,我深知选题既要体现技术含量,又要符合实际教学需求。这个基于SpringBoot的线上教学辅助平台,正是针对新工科背景下教学管理痛点提出的解决方案…

阅读更多 →
Compose Multiplatform LazyGrid 图片网格性能基准:跨 Android、iOS 与 Desktop 的三端同构实现 2026/9/13 20:33:55

Compose Multiplatform LazyGrid 图片网格性能基准:跨 Android、iOS 与 Desktop 的三端同构实现

Compose Multiplatform LazyGrid 图片网格性能基准:跨 Android、iOS 与 Desktop 的三端同构实现 【免费下载链接】compose-multiplatform Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces …

阅读更多 →
Pascal 3D 编辑器垂直模型(Vertical Model)架构详解:层级堆叠、墙体顶面夹持与板面支撑规则 2026/9/13 20:33:55

Pascal 3D 编辑器垂直模型(Vertical Model)架构详解:层级堆叠、墙体顶面夹持与板面支撑规则

Pascal 3D 编辑器垂直模型(Vertical Model)架构详解:层级堆叠、墙体顶面夹持与板面支撑规则 【免费下载链接】editor Open-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.…

阅读更多 →
CCGS 非确定性测试检测指南:用 /test-flakiness 技能定位抖动测试并守护测试套件稳定性 2026/9/13 20:33:55

CCGS 非确定性测试检测指南:用 /test-flakiness 技能定位抖动测试并守护测试套件稳定性

CCGS 非确定性测试检测指南:用 /test-flakiness 技能定位抖动测试并守护测试套件稳定性 【免费下载链接】Claude-Code-Game-Studios Turn Claude Code into a full game dev studio — 49 AI agents, 72 workflow skills, and a complete coordination system mirro…

阅读更多 →
无历史流量数据场景下的 Agent 告警配置:agent-platform-alert-configuration 的流量模式决策与默认策略指南 2026/9/13 20:33:55

无历史流量数据场景下的 Agent 告警配置:agent-platform-alert-configuration 的流量模式决策与默认策略指南

无历史流量数据场景下的 Agent 告警配置:agent-platform-alert-configuration 的流量模式决策与默认策略指南 【免费下载链接】skills Agent Skills for Google products and technologies 项目地址: https://gitcode.com/GitHub_Trending/skills29/skills 本…

阅读更多 →
OI-wiki 实战指南:在 Windows 上使用 WSL 搭建 NOI Linux 兼容的竞赛开发环境 2026/9/13 20:30:54

OI-wiki 实战指南:在 Windows 上使用 WSL 搭建 NOI Linux 兼容的竞赛开发环境

OI-wiki 实战指南:在 Windows 上使用 WSL 搭建 NOI Linux 兼容的竞赛开发环境 【免费下载链接】OI-wiki :star2: Wiki of OI / ICPC for everyone. (某大型游戏线上攻略,内含炫酷算术魔法) 项目地址: https://gitcode.com/GitHu…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞