在公共函数中区分上层调用者的模型

本文探讨了在编程中如何区分上层调用者的需求,通过设置全局标志变量来处理UI界面刷新按钮和DOM事件委派等场景。作者详细介绍了在SlickGrid框架中处理刷新功能和自定义select控件时遇到的问题,以及如何通过flag来区分不同的调用流程。同时,提出了模型的扩展和遗留问题,包括input事件、keyup事件以及公共函数与下层函数调用的处理策略。

一、背景及问题详述:

  在编码时常常会将多次用到的公共函数封装之后,供其他函数调用。而在调用时,免不了需要根据上层调用函数的不同有不同的结果。
  举几个最近遇到的具体的场景:

1.UI界面的刷新按钮

  UI界面的刷新按钮,其功能定义不同于F5的刷新整个页面。而是通过给定的url再次获取页面中指定的表格数据、图片等。以表格为例,为方便用户操作和减轻前端压力,表格常常会有分页、搜索功能。当存在分页功能时,搜索就必须后台处理(除非你想实现这样的效果————只告诉用户当前页有几项匹配,即使在后台保存的数据中存在其他的匹配项,也忽略),此时每一次搜索、翻页都是通过url请求数据来实现的。那刷新按钮需要实现的功能就是,记录最近一次的请求,点击刷新时重新获取一次数据。
  按理说实现起来简单啊,用个变量保存最近使用的url就好了,刷新就取这个变量再请求一次。的确,如果自己从头写请求,写表格的展现方式,这不失为一种解决方式。但我想现在很少有人在工作中会从头写,都是使用第三方的js库。比如,笔者在项目中使用的就是基于SlickGrid(https://github.com/mleibman/SlickGrid)再次封装的框架,该框架不支持动态传入url,取代的是在创建时指定一个baseUrl,在load数据时调用此url,之后若需要实现过滤效果,则需传入一个filter对象(JSON形式),由其内部拼装成baseUrl?key=value&name=tom的形式,再调用此url,可气的是封装好的对象不返回当前使用的url。
   当时为了解决此问题,考虑过修改框架,增加一个方法,将当前使用的url暴露出来。当然这是最后的方法,因为太蠢太暴力。之后详细分析代码,发现两条流程有差异,也有联系。正常获取时,走load方法;过滤时需要先自行拼装json并传入对象,最后还是会调用load方法。
  最后采用设置一个变量flag并初始化为false,当走过滤时,将flag置为true,并保存filter对象,在load执行时的回调函数中,先根据flag的值判断是过滤流程还是直接获取,在处理完相应业务后,再将flag置false。
  整个流程中flag最终都会被置为false,唯一flag为true的时候就是经过过滤,业务处理完之前。
  之前的刷新实现就是根据flag值判断是过滤还是直接获取后,用同一个函数封装处理如过滤则

        func = function(){
                    obj.setFilter(filter);
                    obj.load();
                }
                func = function (){
                    obj.load();
                }

  最后点击刷新时直接调用函数func();

2.DOM元素事件委派

  同样是使用封装过的控件(公司内部使用,样式统一,因信息安全问题无法贴代码),因原生select丑爆了且不可编辑,在封装select控件时,隐藏了select,以input显示,同时在功能上即支持用户下拉选择(通过css样式和事件委派,使用隐藏的select实现),也可支持用户输入(input实现),即界面显示的是一个有着select功能和视觉效果的input框!
  感觉屌屌的,马上问题就来了——参数检查。禁用input的输入还好,选什么就显示什么,怎么检查都好。但同时支持选择和输入时,检查就有问题。监听select的select事件,此事件触发时只是选中了,但值还没有填到input中,只能通过检查select的值来检查。而input的input方法(为了能感知到用户用鼠标复制黏贴而监听的input事件)因为用户输入的,肯定是取input的值检查合理,input事件处理完后会再执行select的select事件(因为有一个小功能,根据输入的input值会匹配下拉框现有的值,类似于百度搜索输入)。此时select用自己的逻辑检测并覆盖之前input的结果。然后悲剧。
  最后同例一一样使用一个flag用以在公共的函数select中判断是哪个流程调用了该方法。区分执行各自的逻辑。

二、模型说明

  抽取公共部分分析:
这里写图片描述
  全局定义flag=false,在每次公共函数结束时将flag置为false,flag只有在经过特性函数,且未退出公共函数时才为true,以此判断这一次流程是否走过特性函数。流程可反复循环的调用。
  将flag换为整数,可以区分多个特性分支到底走了哪条。
  这里写图片描述

三、扩展和遗留问题

1.input的input事件

该事件在标准浏览器中良好支持,IE9以下不支持。需使用IE专用属性onpropertychange
oninput事件在value改变且失去焦点时实时触发,但是通过js改变value时不会触发;
onpropertychange事件是任何属性改变都会触发,每增加或删除一个字符就会触发,通过js改变也会触发该事件

2.keyup事件

keyup事件未参数检测常用的事件。但经测试,该事件在select之前和之后各触发一次。不适用该模型

3.相似模型讨论

先调用公共函数,在调用下层函数(也可能没有下层函数)。
目前有两条思路:
  公共函数处理一遍,若有下层函数,推翻公共函数的结论,以下层函数结果为准(无脑覆盖流)。缺陷,过渡过程有时让人难以接受。还是以参数检测为例,先显示错误提示,再检测一次,发现合法,再隐藏,表现就是用户输入一个字符,错误提示闪一下又没了,坑。
  为了解决上面的问题。考虑过再公共函数中使用一个定时器,我得到结果,但我傲娇,我要等一段时间(比如20ms)在将结果反馈展现。如果后续的下层调用者赞同我,以我的结果为准,定时器超时后显示结果,如果下层调用者结果与我不同,取消定时器,由下层调用者显示结果。(公共函数:意见一致时,我说了算,不一致时,别人说了算~)。
  

4.缺陷与待思考、优化点

只适用于单线程,目前在前端使用足够。但该模型在多线程时需注意同步互斥,具体可参考操作系统关于信号量的表述。
公共函数本身的立意是处理相同的逻辑,现在需在其中根据特性实现不同的功能,实际以违背了最初的用意,若代码全为自己实现,应考虑将对特性的区分处理抽取出来,独立处理。
<think>嗯,用户的问题是关于TR-181数据模型中如何从上层调用模块函数。结合之前的对话历史,用户似乎对TR-181标准中的函数调用机制很感兴趣,特别是类似"set_service"这样的非标准RPC方法。 用户可能是在开发网络设备的管理功能,需要理解如何通过TR-181模型实现上层应用对底层模块的控制。从引用内容看,用户还关注了AUTOSAR架构中的回调函数机制(如CanIf_TrcvModeIndication),这说明ta对模块间通信模式有深入研究需求。 需要重点解释TR-181的两种调用方式:标准参数读写和扩展RPC方法。特别要强调"set_service"这类厂商自定义方法的实现原理,这正好对应了用户之前问的"fw"相关功能。可以结合回调函数的例子(如引用[4]中开发者封装rt_data函数的案例),说明设备厂商如何封装底层操作。 用户提供的四个引用其实都是关于嵌入式系统模块调用的,虽然不直接对应TR-181,但原理相通。比如引用[1]的CanSM模块状态切换,引用[3]的PDU路由机制,引用[4]的回调函数封装,这些都可以类比到TR-181的设备管理场景。不过要注意区分:AUTOSAR是车内通信标准,而TR-181是网络设备管理标准。 在回答时需要澄清一个关键点:TR-181本身不定义函数调用接口,只定义数据模型。真正的调用是通过TR-069/USP协议实现的RPC。这个区别很重要,否则用户可能会混淆数据模型和通信协议的关系。 最后要给出具体示例,比如如何通过USP的Set命令操作"Device.Firewall."对象。考虑到用户之前问过防火墙配置,这个例子应该能帮助ta理解整个调用链条:从上层应用发出RPC请求,到设备执行set_service逻辑,再到修改底层防火墙规则。</think>在TR-181数据模型中实现上层对模块函数调用,主要通过两种机制实现: ### 一、标准参数读写机制(核心方式) TR-181本质定义的是**数据模型**而非函数接口,标准调用方式是通过读写参数实现功能控制: 1. **控制参数** 模块提供特定读写参数作为"函数入口",例如: ```plaintext Device.Firewall.Enable = true // 启用防火墙 Device.WiFi.Radio.1.TransmitPower = 15 // 调整发射功率 ``` 上层通过TR-069/USP协议发送`SetParameterValues`或`Set`指令修改这些参数值,触发模块内部逻辑[^1]。 2. **执行参数(Command键)** 特殊参数类型,写入特定字符串触发动作: ```plaintext Device.DeviceInfo.Reboot() // 写入"1"触发重启 Device.WiFi.AccessPoint.1.Reboot() ``` 本质是通过参数写入模拟函数调用[^3]。 ### 二、扩展RPC方法(厂商自定义) 设备厂商可扩展非标准RPC方法实现复杂操作: 1. **方法定义** 在数据模型中声明扩展方法: ```plaintext Device.Firewall. ├── SetService() // 自定义RPC方法 ├── AddRule() └── DeleteRule() ``` 2. **调用过程** 上层通过TR-069的`Download`或USP的`Execute`指令调用: ```json /* USP示例 */ { "set_service": { "obj_path": "Device.Firewall", "param": {"service": "SSH", "action": "allow"} } } ``` 设备内部通过回调机制执行实际逻辑(类似引用[4]的封装模式)[^4]。 ### 三、典型调用流程(以防火墙为例) ```mermaid sequenceDiagram participant ACS as 上层管理系统 participant Agent as 设备TR-181 Agent participant Module as 防火墙模块 ACS->>Agent: USP Execute(SetService, "SSH") Agent->>Module: 回调函数调用 (如fw_set_service_handler) Note right of Module: 执行实际配置<br>1. 验证参数<br>2. 生成iptables规则<br>3. 写配置文件 Module-->>Agent: 返回操作结果 Agent-->>ACS: 返回执行状态 ``` ### 关键实现要点 1. **回调注册** 模块启动时向TR-181 Agent注册处理函数: ```c // 伪代码示例(类似引用[4]) tr181_register_callback("Device.Firewall.SetService", fw_set_service_handler); ``` 2. **参数映射** 将RPC参数转换为模块内部操作: ```c void fw_set_service_handler(tr181_param_t *params) { char *service = tr181_get_string(params, "service"); char *action = tr181_get_string(params, "action"); iptables_add_rule(service, action); // 实际业务逻辑 } ``` 3. **异步通知** 通过事件机制反馈结果(如`Device.Firewall.LastChange`)[^3]。 ### 四、与AUTOSAR架构的对比 | 特性 | TR-181 | AUTOSAR(引用[1][3]) | |--------------|----------------------------|---------------------------| | 调用机制 | 参数写入/RPC方法 | 模块间API直接调用 | | 通信协议 | TR-069/USP | 内存函数指针 | | 典型应用 | 设备远程管理 | ECU内部通信 | | 回调用途 | 实现厂商扩展功能 | 模块状态通知(如CanTrcv)| > **提示**:实际开发需查阅设备SDK文档,厂商通常提供类似`register_module_handler()`的注册接口(类似RT-Thread的组件初始化[^2])。 --- ### 相关问题 1. TR-181标准中如何定义可执行命令(Command key)的规范?不同厂商的实现有何差异? 2. 在USP协议中如何设计安全的RPC方法调用机制以防止未授权操作? 3. TR-181数据模型与Linux sysfs的交互机制有何异同? 4. 如何通过TR-181实现类似AUTOSAR中PduR模块的网关功能(引用[3])? 5. 设备重启时如何保持通过TR-181配置的动态规则(如防火墙规则)?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值