

1.3层级建立
建立FB_Machine_HW_LOG使用ITF_Machine_HW_LOG自己的接口;
FB_Machine_HW_LOG:
FUNCTION_BLOCK FB_Machine_HW_LOG IMPLEMENTS ITF_Machine_HW_LOG
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
fbLogger : ITF_LoggerToDevLogAdapter ;//建立内部接口
内部:fbLogger : ITF_LoggerToDevLogAdapter ;//建立内部链接接口;

2.2层级的建立
以3层级构建类推:
建立FB_LoggerToDevLogAdapter 使用ITF_LoggerToDevLogAdapter 自己的接口
FUNCTION_BLOCK FB_LoggerToDevLogAdapter IMPLEMENTS ITF_LoggerToDevLogAdapter
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
ipSink : ITF_DevLogSink ;// 设备层 Log 下沉,由应用赋值为 FB_DevLogRouter 等
......
......
......
......
......
......
......
......
......
......
程序块:
(* 默认注入内部 Router,若未通过 M_SetSink 注入外部 Sink *)
IF NOT bSinkInjected THEN
ipSink := fbDevLogRouter;
bSinkInjected := TRUE;
END_IF
内部:ipSink : ITF_DevLogSink ;//建立内部链接接口;

3.1层级的建立
建立FB_DevLogRouter 使用ITF_DevLogSink自己的接口
FUNCTION_BLOCK FB_DevLogRouter IMPLEMENTS ITF_DevLogSink
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR
iRingCount : UDINT ;// 缓存条数
sPathWithOpt : WSTRING(96) ;// 设备路径 + 可选治具/载具/料盘
sLineAssembled : WSTRING(255) ;// 组装后的单行 log
fbLogRingBuffer : FB_StringRingBuffer ;// 环形缓存
aRRsLogBuffer : ARRAY[1..100] OF WSTRING(255) ;// 缓存存储
iLatesDisplayNum: DINT;
//===========================================================================//
itfLogger : ITF_Logger ;//日志
内部: itfLogger : ITF_Logger ;//日志

4.0层级的建立
具体的的实现:

5.最终程序的实现
在最外部程序实现:


FUNCTION_BLOCK FB_StationFoldDispensing
VAR_INPUT
......
......
VAR_OUTPUT
END_VAR
VAR
...........
...........
fbLog : FB_Logger ;//设备日志
fbAoiHwLogAdapter : FB_LoggerToDevLogAdapter ;//AOI 模组-设备层日志适配
fbAoiHwLogRouter : FB_DevLogRouter ;//AOI 设备日志下沉到 FB_Logger
fbMachineHwLog : FB_Machine_HW_LOG ;//AOI 模组-硬件监视日志入口
//=============================================CommFB================================================//
......
......
END_VAR
......
......
......
fbLog() ;
fbLog.M_SaveFile(
sFileName := 'Logs' ,
sRootDirPath := 'C:\UserRecord\DispensingStation' );
//Aoi 硬件日志:适配器 Sink 用工站 Router,Router.pLogger 接本站 fbLog(勿用 ADR,接口直接赋实例) ;
fbAoiHwLogRouter.pLogger := fbLog ;
fbAoiHwLogAdapter.pDevLogSink := fbAoiHwLogRouter ;
fbMachineHwLog.Prop_LoggerToDevLogAdapter := fbAoiHwLogAdapter ;
fbModuleAoi.pMachine_HW_LOG := fbMachineHwLog ;
参考三步法:
1.提供者->2.注入点->3.接收端本地缓存->4.最终调用落点
1.提供者:fbLog()
2.注入点:
fbAoiHwLogRouter.pLogger := fbLog ;
fbAoiHwLogAdapter.pDevLogSink := fbAoiHwLogRouter ;
fbMachineHwLog.Prop_LoggerToDevLogAdapter := fbAoiHwLogAdapter ;
fbModuleAoi.pMachine_HW_LOG := fbMachineHwLog ;
每个里面必须由属性传输出去:依次向下传输:
第一次:0层级给1层级注入:
fbAoiHwLogRouter.pLogger := fbLog ;//FB_Logger-0层级
第二次:1层级给2层级注入:
fbAoiHwLogAdapter.pDevLogSink := fbAoiHwLogRouter ;//FB_DevLogRouter-1层级
第三次:2层级给3层级注入:
fbMachineHwLog.Prop_LoggerToDevLogAdapter := fbAoiHwLogAdapter;//FB_LoggerToDevLogAdapter-2层级
第四次:3层级给最外部注入:
fbModuleAoi.pMachine_HW_LOG := fbMachineHwLog ;//FB_Machine_HW_LOG-3层级
3.接收本地缓存
外部调用:




4.最终落脚点

安全处理:

1) IF ipSink <> 0 THEN ... 是 Adapter 侧的"Sink 是否已注入"
在 FB_LoggerToDevLogAdapter.M_SubmitDevLog 里:
IF ipSink <> 0 THEN
ipSink.M_WriteDevLog(stLog);
END_IF
ipSink的来源:要么你通过属性pDevLogSink.Set注入(ipSink := pDevLogSink;),要么(原设计)FB 本体那段默认注入把它指向内部 Router。- 理解:即使你"通常都会 set 属性",代码仍然要防御式写法,避免某个站点/某个时刻没注入就直接调用接口导致崩。
2) IF itfLogger <> 0 THEN ... 是 Router 侧的"最终 Logger 是否已注入"
在 FB_DevLogRouter.M_WriteDevLog 里:
IF itfLogger <> 0 THEN
itfLogger.M_LoggerWrite(wsToMsg:= sLineAssembled);
END_IF
itfLogger的来源:你在工站里做的fbAoiHwLogRouter.pLogger := fbLog;(这是 Router 的pLogger.Set,内部是itfLogger := pLogger;)。- 理解:即使 Adapter 已经把
ipSink指到 Router 了,如果 Router 没接到底层fbLog,同样也不能写。
3) 为什么"set 了就一定非 0"仍然要写判断?
因为 PLC 扫描周期里赋值、调用可能被别人改顺序/改条件,或者某些模式下不注入(比如某站点不需要落盘日志)。这两个 <>0 判断就是"链路上每一段都允许缺省为空,但为空就不做事、不报错"。
一句话总结
- 属性 set 是"你主动接线";
IF ipSink <> 0/IF itfLogger <> 0是"就算你没接线,也不要崩溃,只是这条日志不输出"。