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;
|