Inhalte

Abstract Factories: OPC-UA HMI Connection

Table of Contents

Introduction

In the example, we use an opc ua interface for the HMI, firstly because it is more difficult than an ADS interface and secondly because it is more flexible because there are many HMI manufacturers that support opc ua. With the opc ua interface, the alarms have to be on the stack to access the resources from outside. Since application developers do create only an alarm object for it, there is a global array in which the active alarms are entered. The global list must also provide an alarm service, since it is a subscriber to the alarm distribution service.

Data Types

We need structured types for our array.

Type Struct AlarmProperties
TYPE AlarmProperties :
STRUCT
	additionalTextListNumber 	:DWORD := 0;
	alarmMessage 				:IAlarmMessageDto := 0;
	class 						:HmiAlarmClass := HmiAlarmClass.INFO;
	displayType 				:AlarmDisplayType := AlarmDisplayType.NO_POPUP_WINDOW;
	functionUnit 				:FunctionUnitName := '';
	hashcode 					:Hashcode := 0;
	myMessageNumber 			:DWORD := 0;
	info 						:AlarmMessageInfo := '';
	name 						:ObjectName := '';
	state 						:HmiAlarmState := HmiAlarmState.INACTIVE;
	stopCategory 				:AlarmStopCategory := AlarmStopCategory.DONT_STOP;
END_STRUCT
END_TYPE
Type Struct HmiAlarmProperties
TYPE HmiAlarmProperties EXTENDS AlarmProperties :
STRUCT
	hmiAlarmActivationBit 		:BOOL := FALSE;
END_STRUCT
END_TYPE

Implementation

Class OpcUaHmiAlarmList
{attribute 'reflection'}
FUNCTION_BLOCK OpcUaHmiAlarmList EXTENDS Object IMPLEMENTS IAlarmMessageService, IRunable
VAR
	{attribute 'OPC.UA.DA' := '1'}
	alarmList				:ARRAY[HmiAlarmClass.INFO..HmiAlarmClass.ERROR] OF ARRAY[FIRST_ALARM..LAST_ALARM] OF HmiAlarmProperties;
	{attribute 'OPC.UA.DA' := '0'}
	alarmListShadow 		:ARRAY[HmiAlarmClass.INFO..HmiAlarmClass.ERROR] OF ARRAY[FIRST_ALARM..LAST_ALARM] OF HmiAlarmProperties;
	{attribute 'OPC.UA.DA.Access' := '1'}
	numberOfActiveAlarms 	:ARRAY[HmiAlarmClass.INFO..HmiAlarmClass.ERROR] OF USINT;
END_VAR
VAR CONSTANT
	FIRST_ALARM 			:USINT := 1;
	LAST_ALARM 				:USINT := 255;
	ALARM_INCREMENT 		:USINT := 1;
	NO_ALARM_IS_ACTIVE 		:USINT := 0;
	ALARM_WAS_NOT_FOUND 	:USINT := 0;
END_VAR
METHOD PROTECTED activateAlarm :BOOL
VAR_INPUT
	alarmMessage :IAlarmMessageDto;
END_VAR
VAR_IN_OUT
	alarm :HmiAlarmProperties;
END_VAR
alarm.hmiAlarmActivationBit := TRUE;
THIS^.setAlarm(
	alarmMessage := alarmMessage,
	alarm := alarm
);
GET PROPERTY className :ClassName


className := 'OpcUaHmiAlarmList';
METHOD clear
VAR_INPUT
	alarmMessage	:IAlarmMessageDto;
END_VAR
IF (THIS^.isObjectValid(alarmMessage)) THEN;
	THIS^.clearAlarm(
		alarmMessage := alarmMessage,
		messagePointer := THIS^.findMessage(alarmMessage)
	);
	THIS^.updateShadow(alarmMessage.class);
END_IF
METHOD PROTECTED clearAlarm
VAR_INPUT
	alarmMessage :IAlarmMessageDto;
	messagePointer :USINT;
END_VAR
VAR
	class :HmiAlarmClass;
	actualAlarm :USINT;
END_VAR
VAR CONSTANT
	EMPTY_ALARM :HmiAlarmProperties := (
		hmiAlarmActivationBit := FALSE,
		additionalTextListNumber := 0,
		alarmMessage := 0,
		class := HmiAlarmClass.INFO,
		displayType := AlarmDisplayType.NO_POPUP_WINDOW,
		functionUnit := '',
		hashcode := 0,
		myMessageNumber := 0,
		info := '',
		name := '',
		state := HmiAlarmState.INACTIVE,
		stopCategory := AlarmStopCategory.DONT_STOP
	);
	
	NEXT_ALARM :USINT := 1;
END_VAR
IF (messagePointer <> THIS^.ALARM_WAS_NOT_FOUND) THEN
	IF (THIS^.isObjectValid(alarmMessage)) THEN
		alarmMessage.state := HmiAlarmState.INACTIVE;
		class := alarmMessage.class;
	END_IF
	
	THIS^.alarmListShadow[class][messagePointer] :=
	THIS^.alarmList[class][messagePointer] :=
	EMPTY_ALARM;
	
	THIS^.decrementActiveAlarms(class);
	
	RETURN(THIS^.numberOfActiveAlarms[class] < THIS^.FIRST_ALARM);
	RETURN(messagePointer > THIS^.numberOfActiveAlarms[class]);
	
	FOR (actualAlarm := messagePointer) TO (THIS^.numberOfActiveAlarms[class]) BY (NEXT_ALARM) DO
		
		THIS^.alarmListShadow[class][actualAlarm] := 
		THIS^.alarmList[class][actualAlarm] := 
		THIS^.alarmList[class][actualAlarm+NEXT_ALARM];
		
	END_FOR
END_IF
METHOD PROTECTED decrementActiveAlarms :USINT
VAR_INPUT
	class :HmiAlarmClass;
END_VAR
THIS^.numberOfActiveAlarms[class] := (
	LIMIT(
		THIS^.NO_ALARM_IS_ACTIVE,
		THIS^.numberOfActiveAlarms[class] - THIS^.ALARM_INCREMENT,
		THIS^.LAST_ALARM
	)
);
decrementActiveAlarms := THIS^.numberOfActiveAlarms[class];
GET PROPERTY PROTECTED errorWasChangedByHmi :BOOL


errorWasChangedByHmi := THIS^.wasMessageClassChangedByHmi(HmiAlarmClass.ERROR);
METHOD PROTECTED findMessage :USINT
VAR_INPUT
	alarmMessage :IAlarmMessageDto;
END_VAR
VAR
	message :USINT;
END_VAR
findMessage := THIS^.ALARM_WAS_NOT_FOUND;
FOR message := THIS^.FIRST_ALARM TO THIS^.numberOfActiveAlarms[alarmMessage.class] DO
	IF (THIS^.alarmList[alarmMessage.class][message].hashcode = alarmMessage.hashcode) THEN
		findMessage := message;
		EXIT;
	END_IF
END_FOR
METHOD handle
VAR_INPUT
	alarmMessage	:IAlarmMessageDto;
END_VAR
RETURN(THIS^.isObjectNull(alarmMessage));
RETURN(THIS^.findMessage(alarmMessage) <> THIS^.ALARM_WAS_NOT_FOUND);
IF (alarmMessage.state < HmiAlarmState.PROVIDED_FOR_HMI) THEN
	THIS^.activateAlarm(
		alarmMessage := alarmMessage,
		alarm := THIS^.alarmList[alarmMessage.class][THIS^.incrementActiveAlarms(alarmMessage.class)]
	);
	THIS^.updateShadow(alarmMessage.class);
END_IF
METHOD PROTECTED handleHmiChangeError
THIS^.handleHmiChangeForClass(HmiAlarmClass.ERROR);
METHOD PROTECTED handleHmiChangeForClass
VAR_INPUT
	class :HmiAlarmClass;
END_VAR
VAR
	actualAlarm :USINT;
END_VAR
FOR actualAlarm := THIS^.FIRST_ALARM TO THIS^.numberOfActiveAlarms[class] DO
	IF (THIS^.wasAlarmChanged(class, actualAlarm)) THEN
		IF (THIS^.alarmList[class][actualAlarm].state = HmiAlarmState.QUIT_ON_HMI) THEN
			THIS^.clearAlarm(
				alarmMessage := THIS^.alarmList[class][actualAlarm].alarmMessage,
				messagePointer := actualAlarm
			);
		ELSE
			THIS^.alarmList[class][actualAlarm].alarmMessage.state :=
			THIS^.alarmListShadow[class][actualAlarm].state :=
			THIS^.alarmList[class][actualAlarm].state;
		END_IF
	END_IF
END_FOR
THIS^.updateShadow(class);
METHOD PROTECTED handleHmiChangeInfo
THIS^.handleHmiChangeForClass(HmiAlarmClass.INFO);
METHOD PROTECTED handleHmiChangeWarning
THIS^.handleHmiChangeForClass(HmiAlarmClass.WARNING);
METHOD PROTECTED incrementActiveAlarms :USINT
VAR_INPUT
	class :HmiAlarmClass;
END_VAR
THIS^.numberOfActiveAlarms[class] := (
	LIMIT(
		THIS^.FIRST_ALARM,
		THIS^.numberOfActiveAlarms[class] + THIS^.ALARM_INCREMENT,
		THIS^.LAST_ALARM
	)
);
incrementActiveAlarms := THIS^.numberOfActiveAlarms[class];
GET PROPERTY PROTECTED infoWasChangedByHmi :BOOL


infoWasChangedByHmi := THIS^.wasMessageClassChangedByHmi(HmiAlarmClass.INFO);
METHOD run
IF (THIS^.errorWasChangedByHmi) THEN
	THIS^.handleHmiChangeError();
END_IF

IF (THIS^.warningWasChangedByHmi) THEN
	THIS^.handleHmiChangeWarning();
END_IF

IF (THIS^.infoWasChangedByHmi) THEN
	THIS^.handleHmiChangeInfo();
END_IF
METHOD PROTECTED setAlarm
VAR_INPUT
	alarmMessage : IAlarmMessageDto;
END_VAR
VAR_IN_OUT
	alarm : HmiAlarmProperties;
END_VAR
alarm.additionalTextListNumber := alarmMessage.additionalTextListNumber;
alarm.alarmMessage := alarmMessage;
alarm.displayType := alarmMessage.displayType;
alarm.functionUnit := alarmMessage.functionUnit;
alarm.info := alarmMessage.info;
alarm.myMessageNumber := alarmMessage.messageNumber;
alarm.name := alarmMessage.name;
alarm.stopCategory := alarmMessage.stopCategory;
alarm.class := alarmMessage.class;
alarm.hashcode := alarmMessage.hashcode;
alarm.state := HmiAlarmState.PROVIDED_FOR_HMI;
alarm.alarmMessage.state := HmiAlarmState.PROVIDED_FOR_HMI;
METHOD PROTECTED updateShadow
VAR_INPUT
	class :HmiAlarmClass;
END_VAR
THIS^.alarmListShadow[class] := THIS^.alarmList[class];
GET PROPERTY PROTECTED warningWasChangedByHmi :BOOL


warningWasChangedByHmi := THIS^.wasMessageClassChangedByHmi(HmiAlarmClass.WARNING);
METHOD PROTECTED wasAlarmChanged  :BOOL
VAR_INPUT
	class :HmiAlarmClass;
	messagePointer :USINT;
END_VAR
wasAlarmChanged := (
	Tc2_System.MEMCMP(
		ADR(THIS^.alarmList[class][messagePointer]),
		ADR(THIS^.alarmListShadow[class][messagePointer]),
		SIZEOF(THIS^.alarmList[class][messagePointer])
	) <> ComparationResult.EQUAL
);
METHOD PROTECTED wasMessageClassChangedByHmi :BOOL
VAR_INPUT
	class :HmiAlarmClass;
END_VAR
wasMessageClassChangedByHmi := (
	Tc2_System.MEMCMP(
		ADR(THIS^.alarmList[class]),
		ADR(THIS^.alarmListShadow[class]),
		SIZEOF(THIS^.alarmList[class])
	) <> ComparationResult.EQUAL
);