Inhalte

Abstract Factories: Object

Table of Contents

Introduction

First, we need an abstract class from which we derive everything and which we can use for queries with __QUERINTERFACE and __QUERYPOINTER. This class will be our object. Our object should:

  • Be uniquely identifiable, for this we generate a 64 bit pseudorandom number, that is then the hashcode of the object.

  • For logging purposes, have an instance name and a class name. This will be the properties name and className.

  • For sorting purposes, it will be able to compare with other objects, and check if it sees another object if it is another object or if it just looks in the mirror. For this, there will be the methods compareTo and isEqual.

  • For checking if other objects have a valid reference, the methods isObjectValid and isObjectNull.

  • In case we need it, it should still have its memory address, for this it has the property address.

  • Implement an interface which extends __SYSTEM.IQueryInterface, for our queries this will be our IObject

Data Types

The alias data types allow us to easily make changes afterward, e.g. it would be conceivable to limit the length of the class name. Okay, let's start with our aliases, for hashcode, class name and object name.

Alias ClassName
TYPE 
	ClassName :Tc2_System.T_MaxString; 
END_TYPE
Alias Hashcode
TYPE 
	Hashcode : ULINT; 
END_TYPE
Alias ObjectName
TYPE 
	ObjectName : Tc2_System.T_MaxString; 
END_TYPE

For the comparison and sorting, we want to find a uniform handling. Therefore, we create an enumeration for the result of a comparison.

Enumeration ComparationResult
{attribute 'qualified_only'}
TYPE ComparationResult :(
	(* procedes in order *)
	SMALLER := -1,
	(* same position in order *)
	EQUAL := 0,
	(* after in order *)
	GREATER := 1
) INT;
END_TYPE

Interfaces

Next we need the interface of the object. We divide the interface into IComparable and IObject for a little more flexibility in the future.

Interface IComparable
INTERFACE IComparable EXTENDS __SYSTEM.IQueryInterface
METHOD compareTo :ComparationResult
VAR_INPUT
	object :IObject;
END_VAR
Interface IObject
INTERFACE IObject EXTENDS __SYSTEM.IQueryInterface, IComparable
PROPERTY address :__XWORD
GET
PROPERTY hashcode :Hashcode
GET

METHOD isEqual :BOOL
VAR_INPUT
	object :IObject;
END_VAR
METHOD isObjectNull :BOOL
VAR_INPUT
	object :IObject;
END_VAR
METHOD isObjectValid :BOOL
VAR_INPUT
	object :IObject;
END_VAR
PROPERTY name :ObjectName
GET

Implementation

Now let's put the things together to the abstract object class, for the hash code we use a xor shift algorithm.

Abstract class Object
FUNCTION_BLOCK ABSTRACT Object IMPLEMENTS IObject
VAR
	myHashcode :Hashcode;
	{attribute 'instance-path'}
	{attribute 'no-init'}
	objectName :ObjectName;
END_VAR
VAR_STAT
	{attribute 'hide'}
	hashstate1 :Hashcode := 1321861022983091513;
	{attribute 'hide'}
	hashstate2 :Hashcode := 3123198108391880477;
	{attribute 'hide'}
	hashstate3 :Hashcode := 1451815097307991481;
	{attribute 'hide'}
	hashstate4 :Hashcode := 5520930533486498032;
END_VAR

METHOD FB_init :BOOL
VAR_INPUT
	(* if TRUE, the retain variables are initialized (warm start / cold start) *)
	bInitRetains :BOOL;
	(* if TRUE, the instance afterwards gets moved into the copy code (online change) *)
	bInCopyCode :BOOL;
END_VAR
VAR
	{attribute 'hide'}
	newHashstate3 :Hashcode;
END_VAR
VAR CONSTANT
	{attribute 'hide'}
	NUMBER_OF_LEFT_SHIFTS :UINT := 17;
	{attribute 'hide'}
	NUMBER_OF_LEFT_ROTATIONS :UINT := 45;
END_VAR

THIS^.myHashcode := (THIS^.hashstate1 + THIS^.hashstate4);
newHashstate3 := SHL(THIS^.hashstate1, NUMBER_OF_LEFT_SHIFTS);
THIS^.hashstate3 := (THIS^.hashstate3 XOR THIS^.hashstate1);
THIS^.hashstate4 := (THIS^.hashstate4 XOR THIS^.hashstate2);
THIS^.hashstate2 := (THIS^.hashstate2 XOR THIS^.hashstate3);
THIS^.hashstate1 := (THIS^.hashstate1 XOR THIS^.hashstate4);
THIS^.hashstate3 := newHashstate3;
THIS^.hashstate4 := ROL(THIS^.hashstate4, NUMBER_OF_LEFT_ROTATIONS);

GET PROPERTY address :__XWORD


address := THIS;
PROPERTY ABSTRACT className :ClassName
GET

METHOD compareTo :ComparationResult
VAR_INPUT
	object	:IObject;
END_VAR

IF (THIS^.isObjectNull(object)) THEN
	compareTo := ComparationResult.GREATER;
ELSE
	compareTo := ComparationResult.EQUAL;
END_IF

GET PROPERTY hashcode :Hashcode


hashcode := THIS^.myHashcode;
METHOD isEqual :BOOL
VAR_INPUT
	object :IObject;
END_VAR

IF (THIS^.isObjectValid(object)) THEN
	isEqual := (object.hashcode = THIS^.hashcode);
ELSE
	isEqual := FALSE;
END_IF

METHOD isObjectNull :BOOL
VAR_INPUT
	object :IObject;
END_VAR

isObjectNull := (NOT THIS^.isObjectValid(object));

METHOD isObjectValid :BOOL
VAR_INPUT
	object :IObject;
END_VAR
VAR
	{attribute 'displaymode':='hex'}
	objectAddress	:POINTER TO __XWORD;
	memoryArea		:Tc2_System.E_TcMemoryArea;
	testObject		:IObject;
	exc				: __SYSTEM.ExceptionCode;
	{attribute 'displaymode':='hex'}
	stackAddress	:__XWORD;
END_VAR
VAR CONSTANT
	{attribute 'displaymode':='hex'}
	NULL :__XWORD := 0;
	{attribute 'displaymode':='hex'}
	REGISTER_SIZE :UDINT := SIZEOF(__XWORD);
	(* 0x400000 is 4096kb and this is the max configurable stack size *)
	{attribute 'displaymode':='hex'}
	MAX_STACK_SIZE :__XWORD := 16#0040_0000;
	{attribute 'displaymode':='hex'}
	WELL_ALIGNED :__XWORD := 0;
END_VAR
objectAddress := ADR(object);
memoryArea := Tc2_System.F_CheckMemoryArea(pData := objectAddress^, nSize := REGISTER_SIZE);
CASE memoryArea OF
Tc2_System.E_TcMemoryArea.CNC,
Tc2_System.E_TcMemoryArea.Dynamic,
Tc2_System.E_TcMemoryArea.Static:
	isObjectValid := ((objectAddress^ MOD REGISTER_SIZE) = WELL_ALIGNED);
ELSE
	stackAddress := ADR(stackAddress);
	isObjectValid := (
		(objectAddress^ <> NULL)
		AND_THEN (objectAddress^ >= ABS(stackAddress - MAX_STACK_SIZE))
		AND_THEN ((objectAddress^ MOD REGISTER_SIZE) = WELL_ALIGNED)
	);
END_CASE

RETURN(NOT isObjectValid);

{IF hasvalue(RegisterSize, '32')}
__TRY
	isObjectValid := (__QUERYINTERFACE(object, testObject));
__CATCH(exc)
	isObjectValid := FALSE;
__ENDTRY
{ELSE}
	isObjectValid := (__QUERYINTERFACE(object, testObject));
{END_IF}

GET PROPERTY name :ObjectName


name := THIS^.objectName;