Skip to main content

同步应用实例

在此示例应用程序中,应用程序控制冗余系统中的数据同步。该示例显示了如何通过库 POU 触发同步阶段以及如何评估冗余系统的状态数据。可视化显示这些状态并提供冗余命令。可视化是用户的界面。

冗余系统包括两个 REDU01REDU01 CODESYS Control Win 控制器。可视化运行在 CODESYS TargetVisu.

配置

设备树中的对象

设置

顶层 Device

REDU01

rdncy_img_example_device_config.png

任务配置

rdncy_img_example_task_config.png

冗余配置

可编程逻辑控制器 2

REDU02

一般的

rdncy_img_example_general_config.png

冗余链路

rdncy_img_example_link_config.png

可视化

rdncy_img_example_visu_link_config.png

注册区域

rdncy_img_settings_registered_areas.png

全球数据

GVL

(* Variables synchronized at Redundancy synchronization. Synchronization happens
- on bootup of the second PLC
- on call to RedundancySynchronizeData
- in every task cycle if setting is active in cfg file of both PLCs:
[CmpRedundancy]
DataSyncAlways=1
*)

VAR_GLOBAL
    globalvar1: INT;
    arr1 : ARRAY [0..10000] OF DWORD;
    arr2 : ARRAY [0..10000] OF DWORD;
    arr3 : ARRAY [0..10000] OF DWORD;
    arr4 : ARRAY [0..10000] OF DWORD;
    bSyncDataResult: BOOL;
END_VAR

GVL_1

VAR_GLOBAL
    sReduState: RedundancyState;
    conni: REDUNDANCY_CONNECTION_INFO;
    sSyncInfo: SYNC_INFO;
    sPlcIdent: PLC_IDENT;
    bConnectionState: BOOL;

    tLast: TIME;
    nTimeWarp: INT;
    tDiff: TIME;
    nTimeRun: INT;
    tNow: TIME;
    sState: STRING;
    acopy: INT;
    MB2 AT %MX0.2: BOOL;
    tDiffMax: TIME;
    tNow2: TIME;
    tDiff2: TIME;
    nTimeWarp2: INT;
    tDiffMax2: TIME;
    tLast2: TIME;
    tNowDownloadDone: TIME;
    tNowCodeInitDone: TIME;
    nCycle: INT;
    atNow: ARRAY [0..9] OF TIME;
    nCycle2: INT;
    atNow2: ARRAY [0..9] OF TIME;
    bAreaRegisterDone: BOOL;
    pIB64: POINTER TO BYTE;
    bUpdateData: BOOL;
    bSwitchToStandby: BOOL;
    bSwitchToActive: BOOL;
    bSwitchToSimulation: BOOL;
    bSynchronize: BOOL;
    bSwitchToStandaloneResult: BOOL;
END_VAR

GVL_Synchro

(* Global variables, registered to be synchronized in every task cycle. *)
VAR_GLOBAL
    iSyncFirst: INT;
    iSyncCount: INT;
    iSyncLast: INT;
END_VAR

用于控制同步的 POU

PROGRAM PLC_PRG
VAR
    a: INT;
    bSyncCalled: BOOL;
    Timestamp1: systime.SYSTIME;
    firstcycle: BOOL := TRUE;
    Timestamp2: systime.SYSTIME;
    b: INT;
    sText: STRING;
    bCopy: BOOL;
    Blink: TON;
    bBlink: BOOL;
    bAutoSync: BOOL := TRUE; // Flag to enable autosync mechanism
    xResSync: BOOL;
END_VARIF a = 0 THEN
    Blink(PT := T#1S, IN := TRUE);
END_IF
a := a + 6;
globalvar1 := globalvar1 + 1;

(* Call functions of redundancy library *)
GetRedundancyState(ADR(sReduState));
bConnectionState := GetConnectionState();
sPlcIdent := GetPlcIdent();

(* Call functions of redundancy connection library *)
GetConnectionInfo(ADR(conni));

GetSyncInformation(ADR(sSyncInfo));

// Flag to enable autosync mechanism
IF bAutoSync THEN
    IF sReduState.eRedundancyState = RDCY.STATE.RS_CYCLE_STANDALONE AND bConnectionState AND xResSync = FALSE AND sPlcIdent = RDCY.PLC_IDENT.PLC_ID_2 THEN
            // Call of Synchronize from redundant application. Note, actual synchronisation is executed from background loop with some delay
            xResSync := RDCY.Synchronize();
    END_IF
END_IF

// Recover from error state
(*IF sReduState.eRedundancyState = RDCY.STATE.RS_SYNCHRO_ERROR THEN
    bSwitchToStandaloneResult := SwitchToStandalone();
END_IF

IF sReduState.eRedundancyState = RDCY.STATE.RS_CYCLE_ACTIVE THEN
    bSyncDataResult := RedundancySynchronizeData();
END_IF
*)

IF bUpdateData THEN
    bUpdateData := FALSE;
    RedundancySynchronizeData();
END_IF

IF bSwitchToStandby THEN
    bSwitchToStandby := FALSE;
    SwitchToStandby();
END_IF

IF bSwitchToActive THEN
    bSwitchToActive := FALSE;
    SwitchToActive();
END_IF

IF bSwitchToSimulation THEN
    bSwitchToSimulation := FALSE;
    SwitchToSimulation();
END_IF

IF bSynchronize THEN
    bSynchronize := FALSE;
    Synchronize();
END_IF

Blink();
IF Blink.Q THEN
    Blink.IN := FALSE;
    Blink();
    Blink.IN := TRUE;
    bBlink := NOT bBlink;
END_IF
%IB64;

IF bCopy THEN
    bCopy := FALSE;
    acopy := a;
END_IF

arr2 := arr1;

IF sReduState.eRedundancyState = RDCY.STATE.RS_CYCLE_ACTIVE THEN
    sState := 'Active';
    b := b + 1;
    pIB64 := ADR(%IB64);
    pIB64^ := pIB64^ + 1;
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_CYCLE_STANDBY THEN
    sState := 'Passive';
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_CYCLE_STANDALONE THEN
    sState := 'Standalone';
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_SIMULATION THEN
    sState := 'Simulation';
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_SYNCHRO THEN
    sState := 'Synchro';
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_SYNCHRO_ERROR THEN
    sState := 'SYNCHRO_ERROR';
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_NO_LICENSE THEN
    sState := 'LICENCE EXPIRED';
ELSIF sReduState.eRedundancyState = RDCY.STATE.RS_START THEN
    sState := 'START';
ELSE
    sState := 'Other';
END_IF

nCycle := nCycle + 1;
PROGRAM POU
VAR
END_VAR

tNow2 := TIME();
tDiff2 := tNow - tLast;
IF tDiff2 > T#1D THEN
     nTimeWarp2 := nTimeWarp2 + 1;
     LogAdd(STD_LOGGER, 'IEC', 0, 0, 0, 'Time Warp');
END_IF
IF tDiff2 > tDiffMax2 THEN
     tDiffMax2 := tDiff2;
END_IF
tLast2 := TIME();

IF nCycle2 < 10 THEN
    atNow2[nCycle2] := tNow2;
END_IF

nCycle2 := nCycle2 + 1;
FUNCTION cdCodeInit : DWORD
VAR_IN_OUT
    EventPrm: CmpApp.EVTPARAM_CmpApp;
END_VAR
VAR
END_VAR
tNowCodeInitDone := TIME();
FUNCTION cbDownloadDone : DWORD
VAR_IN_OUT
    EventPrm: CmpApp.EVTPARAM_CmpApp;
END_VAR
VAR
END_VAR
(* Register GVL_Synchro to be synchronized in every task cycle *)
bAreaRegisterDone := AreaRegister(ADR(iSyncFirst), ADR(iSyncLast) - ADR(iSyncFirst) + sizeof(iSyncLast), AREA_TYPE.AREA_SYNCHRO);
tNowDownloadDone := TIME();

可视化

rdncy_img_example_visu.png