Initial import of TCAP project from Motivity.
|
@ -0,0 +1,17 @@
|
|||
|
||||
.PHONY: all
|
||||
all:
|
||||
cd asn_src && $(MAKE)
|
||||
cd src && $(MAKE)
|
||||
cd doc && $(MAKE)
|
||||
|
||||
.PHONY: doc
|
||||
doc:
|
||||
cd doc && $(MAKE) $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
cd asn_src && $(MAKE) $@
|
||||
cd src && $(MAKE) $@
|
||||
cd doc && $(MAKE) $@
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
ROSEINCLUDES = -I ../itu
|
||||
|
||||
EBIN = ../../ebin/ansi
|
||||
|
||||
ERLC = erlc
|
||||
ERLCFLAGS = -b beam -W -v -o $(EBIN)
|
||||
|
||||
ASNC = $(ERLC)
|
||||
ASNCFLAGS = -W -b ber_bin +optimize +driver +noobj $(ROSEINCLUDES)
|
||||
|
||||
$(EBIN)/%.beam:%.erl
|
||||
$(ERLC) $(ERLCFLAGS) $<
|
||||
|
||||
.SECONDARY: %.erl
|
||||
%.erl:%.asn
|
||||
$(ASNC) $(ASNCFLAGS) $<
|
||||
|
||||
.PHONY: default
|
||||
default: all
|
||||
|
||||
.PHONY: all
|
||||
all: $(EBIN)/TR.beam
|
||||
# $(EBIN)/TCAPPackage.beam \
|
||||
# $(EBIN)/TCAP-Remote-Operations-Information-Objects.beam
|
||||
|
||||
TCAPPackage.erl: $(EBIN)/TCAP-Remote-Operations-Information-Objects.beam
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
- rm -f *.asn1db
|
||||
- rm -f $(EBIN)/TR.beam
|
||||
# $(EBIN)/TCAPPackage.beam \
|
||||
# $(EBIN)/TCAP-Remote-Operations-Information-Objects.beam
|
||||
- rm -f TR.erl TCAPPackage.erl
|
||||
- rm -f TR.hrl TCAPPackage.hrl
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
TCAP-Remote-Operations-Information-Objects
|
||||
{iso(1) memberbody(2) usa(840) t1-114(10013) modules(0) informationObjects(1) version4(4)} DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
--Exports Everything
|
||||
IMPORTS
|
||||
emptyBind, emptyUnbind
|
||||
FROM Remote-Operations-Useful-Definitions
|
||||
{joint-iso-ccitt remote-operations(4) useful-definitions(7) version1(0)};
|
||||
|
||||
OPERATION ::= CLASS {
|
||||
&ArgumentType OPTIONAL,
|
||||
&argumentTypeOptional BOOLEAN OPTIONAL,
|
||||
&returnResult BOOLEAN DEFAULT TRUE,
|
||||
&ResultType OPTIONAL,
|
||||
&resultTypeOptional BOOLEAN OPTIONAL,
|
||||
&Errors ERROR OPTIONAL,
|
||||
&Linked OPERATION OPTIONAL,
|
||||
&synchronous BOOLEAN DEFAULT FALSE,
|
||||
&alwaysReturns BOOLEAN DEFAULT TRUE,
|
||||
&InvokePriority Priority OPTIONAL,
|
||||
&ResultPriority Priority OPTIONAL,
|
||||
&invokeLast BOOLEAN DEFAULT FALSE,
|
||||
&operationCode OperationCode UNIQUE OPTIONAL
|
||||
} WITH SYNTAX {
|
||||
[ARGUMENT &ArgumentType [OPTIONAL &argumentTypeOptional]]
|
||||
[RESULT &ResultType [OPTIONAL &resultTypeOptional]]
|
||||
[RETURN RESULT &returnResult]
|
||||
[ERRORS &Errors]
|
||||
[LINKED &Linked]
|
||||
[SYNCHRONOUS &synchronous]
|
||||
[ALWAYS RETURNS &alwaysReturns]
|
||||
[INVOKE PRIORITY &InvokePriority]
|
||||
[RESULT PRIORITY &ResultPriority]
|
||||
[LAST &invokeLast]
|
||||
[CODE &operationCode]
|
||||
}
|
||||
|
||||
ERROR ::= CLASS {
|
||||
&ParameterType OPTIONAL,
|
||||
¶meterTypeOptional BOOLEAN OPTIONAL,
|
||||
&ErrorPriority Priority OPTIONAL,
|
||||
&errorCode ErrorCode UNIQUE OPTIONAL
|
||||
} WITH SYNTAX {
|
||||
[PARAMETER &ParameterType [OPTIONAL ¶meterTypeOptional]]
|
||||
[PRIORITY &ErrorPriority]
|
||||
[CODE &errorCode]
|
||||
}
|
||||
|
||||
OPERATION-PACKAGE ::= CLASS {
|
||||
&Both OPERATION OPTIONAL,
|
||||
&Consumer OPERATION OPTIONAL,
|
||||
&Supplier OPERATION OPTIONAL,
|
||||
&id OBJECT IDENTIFIER UNIQUE OPTIONAL
|
||||
} WITH SYNTAX {
|
||||
[OPERATIONS &Both]
|
||||
[CONSUMER INVOKES &Supplier]
|
||||
[SUPPLIER INVOKES &Consumer]
|
||||
[ID &id]
|
||||
}
|
||||
|
||||
CONNECTION-PACKAGE ::= CLASS {
|
||||
&bind OPERATION DEFAULT emptyBind,
|
||||
&unbind OPERATION DEFAULT emptyUnbind,
|
||||
&responderCanUnbind BOOLEAN DEFAULT FALSE,
|
||||
&unbindCanFail BOOLEAN DEFAULT FALSE,
|
||||
&id OBJECT IDENTIFIER UNIQUE OPTIONAL
|
||||
} WITH SYNTAX {
|
||||
[BIND &bind]
|
||||
[UNBIND &unbind]
|
||||
[RESPONDER UNBIND &responderCanUnbind]
|
||||
[FAILURE TO UNBIND &unbindCanFail]
|
||||
[ID &id]
|
||||
}
|
||||
|
||||
CONTRACT ::= CLASS {
|
||||
&connection CONNECTION-PACKAGE OPTIONAL,
|
||||
&OperationsOf OPERATION-PACKAGE OPTIONAL,
|
||||
&InitiatorConsumerOf OPERATION-PACKAGE OPTIONAL,
|
||||
&InitiatorSupplierOf OPERATION-PACKAGE OPTIONAL,
|
||||
&id OBJECT IDENTIFIER UNIQUE OPTIONAL
|
||||
} WITH SYNTAX {
|
||||
[CONNECTION &connection]
|
||||
[OPERATIONS OF &OperationsOf]
|
||||
[INITIATOR CONSUMER OF &InitiatorConsumerOf]
|
||||
[RESPONDER CONSUMER OF &InitiatorSupplierOf]
|
||||
[ID &id]
|
||||
}
|
||||
|
||||
ROS-OBJECT-CLASS ::= CLASS {
|
||||
&Is ROS-OBJECT-CLASS OPTIONAL,
|
||||
&Initiates CONTRACT OPTIONAL,
|
||||
&Responds CONTRACT OPTIONAL,
|
||||
&InitiatesAndResponds CONTRACT OPTIONAL,
|
||||
&id OBJECT IDENTIFIER UNIQUE
|
||||
} WITH SYNTAX {
|
||||
[IS &Is]
|
||||
[BOTH &InitiatesAndResponds]
|
||||
[INITIATES &Initiates]
|
||||
[RESPONDS &Responds]
|
||||
ID &id
|
||||
}
|
||||
|
||||
OperationCode ::= CHOICE {
|
||||
national [PRIVATE 16] IMPLICIT INTEGER(-32768..32767),
|
||||
private [PRIVATE 17] IMPLICIT INTEGER
|
||||
}
|
||||
|
||||
ErrorCode ::= CHOICE {
|
||||
national [PRIVATE 19] INTEGER(-128..127),
|
||||
private [PRIVATE 20] INTEGER
|
||||
}
|
||||
|
||||
Priority ::= INTEGER(0..MAX)
|
||||
|
||||
END --end of Information Object Specifications
|
|
@ -0,0 +1,211 @@
|
|||
TCAPPackage
|
||||
{iso(1) memberbody(2) usa(840) t1-114(10013) modules(0) tcapPackage(0) version4(4)} DEFINITIONS ::=
|
||||
|
||||
BEGIN
|
||||
|
||||
IMPORTS
|
||||
OPERATION, ERROR
|
||||
FROM TCAP-Remote-Operations-Information-Objects
|
||||
{iso(1) memberbody(2) usa(840) t1-114(10013) modules(0) informationObjects(1) version4(4)};
|
||||
|
||||
PackageType ::= CHOICE {
|
||||
unidirectional [PRIVATE 1] IMPLICIT UniTransactionPDU,
|
||||
queryWithPerm [PRIVATE 2] IMPLICIT TransactionPDU,
|
||||
queryWithoutPerm [PRIVATE 3] IMPLICIT TransactionPDU,
|
||||
response [PRIVATE 4] IMPLICIT TransactionPDU,
|
||||
conversationWithPerm [PRIVATE 5] IMPLICIT TransactionPDU,
|
||||
conversationWithoutPerm [PRIVATE 6] IMPLICIT TransactionPDU,
|
||||
abort [PRIVATE 22] IMPLICIT Abort
|
||||
}
|
||||
|
||||
UniTransactionPDU ::= SEQUENCE {
|
||||
identifier TransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
componentPortion ComponentSequence
|
||||
}
|
||||
|
||||
TransactionPDU ::= SEQUENCE {
|
||||
identifier TransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
componentPortion ComponentSequence OPTIONAL
|
||||
}
|
||||
-- TransactionPDU should include either a Dialogue Portion,
|
||||
-- a Component Sequence or both
|
||||
|
||||
TransactionID ::= [PRIVATE 7] IMPLICIT OCTET STRING
|
||||
-- 0 octets for the Unidirectional, 4 octets for Query, Response & Abort
|
||||
-- 8 octets for Conversation in the order Originating then Responding TID
|
||||
|
||||
Abort ::= SEQUENCE {
|
||||
identifier TransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
causeInformation CHOICE {
|
||||
abortCause P-Abort-cause,
|
||||
userInformation UserAbortInformation
|
||||
} OPTIONAL
|
||||
}
|
||||
-- When the Abort package is generated by the Transaction sublayer,
|
||||
-- the P-Abort-cause must be present
|
||||
|
||||
P-Abort-cause ::= [PRIVATE 23] IMPLICIT INTEGER {
|
||||
unrecognizedPackageType(1),
|
||||
incorrectTransactionPortion(2),
|
||||
badlyStructuredTransactionPortion(3),
|
||||
unassignedRespondingTransactionID(4),
|
||||
permissionToReleaseProblem(5), -- for further study
|
||||
resourceUnavailable(6),
|
||||
unrecognizedDialoguePortionID(7),
|
||||
badlyStructuredDialoguePortion(8),
|
||||
missingDialoguePortion(9),
|
||||
inconsistentDialoguePortion(10)
|
||||
}
|
||||
|
||||
DialoguePortion ::= [PRIVATE 25] IMPLICIT SEQUENCE {
|
||||
version ProtocolVersion OPTIONAL,
|
||||
applicationContext CHOICE {
|
||||
integerApplicationId IntegerApplicationContext,
|
||||
objectApplicationId ObjectIDApplicationContext
|
||||
} OPTIONAL,
|
||||
userInformation UserInformation OPTIONAL,
|
||||
securityContext CHOICE {
|
||||
integerSecurityId [0] IMPLICIT INTEGER,
|
||||
objectSecurityId [1] IMPLICIT OBJECT IDENTIFIER
|
||||
} OPTIONAL,
|
||||
confidentiality [2] IMPLICIT Confidentiality OPTIONAL
|
||||
}
|
||||
|
||||
ProtocolVersion ::= [PRIVATE 26] IMPLICIT OCTET STRING(SIZE (1))
|
||||
-- 0000 0000 not used
|
||||
-- 0000 0001 T1.114-1996
|
||||
-- 0000 0010 T1.114-2000
|
||||
-- other reserved
|
||||
-- These values can be combined using the bit-wise logical or operation
|
||||
-- to indicate support for more than one version, e.g. the value 0000 0011
|
||||
-- means that both 1996 and 2000 versions are supported
|
||||
|
||||
IntegerApplicationContext ::= [PRIVATE 27] IMPLICIT INTEGER
|
||||
|
||||
ObjectIDApplicationContext ::= [PRIVATE 28] IMPLICIT OBJECT IDENTIFIER
|
||||
|
||||
UserInformation ::= [PRIVATE 29] IMPLICIT SEQUENCE OF EXTERNAL
|
||||
|
||||
Confidentiality ::= SEQUENCE {
|
||||
confidentialityId CHOICE {
|
||||
integerConfidentialityId [0] IMPLICIT INTEGER,
|
||||
objectConfidentialityId [1] IMPLICIT OBJECT IDENTIFIER
|
||||
} OPTIONAL,
|
||||
...
|
||||
-- The extension marker indicates the possible presence of items
|
||||
-- in the confidentiality set that are used by the confidentiality
|
||||
-- algorithm.
|
||||
}
|
||||
|
||||
UserAbortInformation ::= [PRIVATE 24] EXTERNAL
|
||||
|
||||
ComponentSequence ::= [PRIVATE 8] IMPLICIT SEQUENCE OF ComponentPDU{InvokeId, {Invocable}, {Returnable}}
|
||||
-- Component Portion specification starts below
|
||||
ComponentPDU{InvokeId:InvokeIdSet, OPERATION:Invocable, OPERATION:Returnable}
|
||||
::= CHOICE {
|
||||
invokeLast [PRIVATE 9] IMPLICIT Invoke{{InvokeIdSet}, {Invocable}}
|
||||
(CONSTRAINED BY { -- invocable.&invokeLast must be TRUE --}
|
||||
! RejectProblem:generalincorrectComponentPortion),
|
||||
returnResultLast [PRIVATE 10] IMPLICIT ReturnResult{{Returnable}},
|
||||
returnError [PRIVATE 11] IMPLICIT ReturnError{{Errors {{Returnable}}}},
|
||||
reject [PRIVATE 12] IMPLICIT Reject,
|
||||
invokeNotLast [PRIVATE 13] IMPLICIT Invoke{{InvokeIdSet}, {Invocable}}
|
||||
(CONSTRAINED BY { -- invocable.&invokeLast must be FALSE --}
|
||||
! RejectProblem:generalincorrectComponentPortion),
|
||||
returnResultNotLast [PRIVATE 14] IMPLICIT ReturnResult{{Returnable}}
|
||||
} (CONSTRAINED BY {-- must conform to the above definition --} ! RejectProblem:generalunrecognisedComponentType)
|
||||
|
||||
Invoke{InvokeId:InvokeIdSet, OPERATION:Operations} ::= SEQUENCE {
|
||||
componentIDs [PRIVATE 15] IMPLICIT OCTET STRING(SIZE (0..2))
|
||||
-- The invoke ID precedes the correlation id. There may be no
|
||||
-- identifier,only an invoke ID, or both invoke and correlation ID.
|
||||
(CONSTRAINED BY {-- must be unambiguous --}
|
||||
! RejectProblem:invokeduplicateInvocation)
|
||||
(CONSTRAINED BY {-- correlation ID must identify an outstanding operation --}
|
||||
! RejectProblem:invokeunrecognisedCorrelationId) OPTIONAL,
|
||||
-- operationCode
|
||||
opcode OPERATION.&operationCode ({Operations} ! RejectProblem:invokeunrecognisedOperation),
|
||||
-- OPERATION.&ParameterType
|
||||
parameter OPERATION.&ResultType ({Operations}{@opcode}
|
||||
! RejectProblem:invoke-mistypedArgument) OPTIONAL
|
||||
} (CONSTRAINED BY {-- must conform to the above definition --} ! RejectProblem:generalincorrectComponentPortion)
|
||||
(CONSTRAINED BY {-- must have consistent encoding --} ! RejectProblem:generalbadlyStructuredCompPortion)
|
||||
(CONSTRAINED BY {-- must conform to T1.114.3 encoding rules --} ! RejectProblem:generalincorrectComponentCoding)
|
||||
|
||||
ReturnResult{OPERATION:Operations} ::= SEQUENCE {
|
||||
componentID [PRIVATE 15] IMPLICIT OCTET STRING(SIZE (1))
|
||||
(CONSTRAINED BY {-- must be that of an outstanding operation --}
|
||||
! RejectProblem:returnResultunrecognisedCorrelationId)
|
||||
(CONSTRAINED BY {-- which returns a result --}
|
||||
! RejectProblem:returnResultunexpectedReturnResult),
|
||||
parameter OPERATION.&ResultType
|
||||
-- ({Operations}{@opcode} !RejectProblem:returnResultincorrectParameter)
|
||||
OPTIONAL
|
||||
} (CONSTRAINED BY {-- must conform to the above definition --} ! RejectProblem:generalincorrectComponentPortion)
|
||||
(CONSTRAINED BY {-- must have consistent encoding --} ! RejectProblem:generalbadlyStructuredCompPortion)
|
||||
(CONSTRAINED BY {-- must conform to T1.114.3 encoding rules --} ! RejectProblem:generalincorrectComponentCoding)
|
||||
|
||||
ReturnError{ERROR:Errors} ::= SEQUENCE {
|
||||
componentID [PRIVATE 15] IMPLICIT OCTET STRING(SIZE (1))
|
||||
(CONSTRAINED BY {-- must be that of an outstanding operation --}
|
||||
! RejectProblem:returnErrorunrecognisedCorrelationId)
|
||||
(CONSTRAINED BY {-- which returns an error --}
|
||||
! RejectProblem:returnErrorunexpectedReturnError),
|
||||
errorCode ERROR.&errorCode({Errors} ! RejectProblem:returnErrorunrecognisedError)
|
||||
(CONSTRAINED BY {-- must be in the &Errors field of the associated operation --}
|
||||
! RejectProblem:returnErrorunexpectedError),
|
||||
parameter ERROR.&ParameterType ({Errors}{@errorCode} !RejectProblem:returnErrorincorrectParameter) OPTIONAL
|
||||
} (CONSTRAINED BY {-- must conform to the above definition --} ! RejectProblem:generalincorrectComponentPortion)
|
||||
(CONSTRAINED BY {-- must have consistent encoding --} ! RejectProblem:generalbadlyStructuredCompPortion)
|
||||
(CONSTRAINED BY {-- must conform to T1.114.3 encoding rules --} ! RejectProblem:generalincorrectComponentCoding)
|
||||
|
||||
Reject ::= SEQUENCE {
|
||||
componentID [PRIVATE 15] IMPLICIT OCTET STRING(SIZE (0..1)),
|
||||
rejectProblem [PRIVATE 21] IMPLICIT Problem,
|
||||
parameter CHOICE {
|
||||
paramSequence [PRIVATE 16] IMPLICIT SEQUENCE {},
|
||||
paramSet [PRIVATE 18] IMPLICIT SET {}
|
||||
-- The choice between paramSequence and paramSet is implementation
|
||||
-- dependent, however paramSequence is preferred.
|
||||
}
|
||||
} (CONSTRAINED BY {-- must conform to the above definition --} ! RejectProblem:generalincorrectComponentPortion)
|
||||
(CONSTRAINED BY {-- must have consistent encoding --} ! RejectProblem:generalbadlyStructuredCompPortion)
|
||||
(CONSTRAINED BY {-- must conform to T1.114.3 encoding rules --} ! RejectProblem:generalincorrectComponentCoding)
|
||||
|
||||
-- PROBLEMS, the specification of Problems follows
|
||||
Problem ::= INTEGER {
|
||||
generalunrecognisedComponentType(257),
|
||||
generalincorrectComponentPortion(258),
|
||||
generalbadlyStructuredCompPortion(259),
|
||||
generalincorrectComponentCoding(260),
|
||||
invokeduplicateInvocation(513),
|
||||
invokeunrecognisedOperation(514),
|
||||
invokeincorrectParameter(515),
|
||||
invokeunrecognisedCorrelationID(516),
|
||||
returnResultunrecognisedCorrelationID(769),
|
||||
returnResultunexpectedReturnResult(770),
|
||||
returnResultincorrectParameter(771),
|
||||
returnErrorunrecognisedCorrelationID(1025),
|
||||
returnErrorunexpectedReturnError(1026),
|
||||
returnErrorunrecognisedError(1027),
|
||||
returnErrorunexpectedError(1028),
|
||||
returnErrorincorrectParameter(1029),
|
||||
-- Applications using T1.114-1988 report Transaction portion
|
||||
-- problems using a Reject component with a problem code in
|
||||
-- the range 1281e6
|
||||
-- It is preferred that other applications report
|
||||
-- these problems using the Abort package type
|
||||
transactionunrecognizedPackageType(1281),
|
||||
transactionincorrectTransPortion(1282),
|
||||
transactionbadlyStructuredTransPortion(1283),
|
||||
transactionunassignedRespondingTransID(1284),
|
||||
transactionpermissionToReleaseProblem(1285),
|
||||
transactionresourceUnavailable(1286)
|
||||
}
|
||||
|
||||
TCInvokeIdSet ::= InvokeId(WITH COMPONENTS { present (-128..127) })
|
||||
|
||||
END
|
|
@ -0,0 +1,103 @@
|
|||
TR
|
||||
{joint-iso-itu-t(2) country(16) ca(124) motivity(113594) tcap(10) modules(0) ansi(1) tr(0) version1(1)} DEFINITIONS ::=
|
||||
|
||||
BEGIN
|
||||
|
||||
PackageType ::= CHOICE {
|
||||
unidirectional [PRIVATE 1] IMPLICIT UniTransactionPDU,
|
||||
queryWithPerm [PRIVATE 2] IMPLICIT TransactionPDU,
|
||||
queryWithoutPerm [PRIVATE 3] IMPLICIT TransactionPDU,
|
||||
response [PRIVATE 4] IMPLICIT TransactionPDU,
|
||||
conversationWithPerm [PRIVATE 5] IMPLICIT TransactionPDU,
|
||||
conversationWithoutPerm [PRIVATE 6] IMPLICIT TransactionPDU,
|
||||
abort [PRIVATE 22] IMPLICIT Abort
|
||||
}
|
||||
|
||||
UniTransactionPDU ::= SEQUENCE {
|
||||
identifier TransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
componentPortion ComponentSequence
|
||||
}
|
||||
|
||||
TransactionPDU ::= SEQUENCE {
|
||||
identifier TransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
componentPortion ComponentSequence OPTIONAL
|
||||
}
|
||||
-- TransactionPDU should include either a Dialogue Portion,
|
||||
-- a Component Sequence or both
|
||||
|
||||
TransactionID ::= [PRIVATE 7] IMPLICIT OCTET STRING
|
||||
-- 0 octets for the Unidirectional, 4 octets for Query, Response & Abort
|
||||
-- 8 octets for Conversation in the order Originating then Responding TID
|
||||
|
||||
Abort ::= SEQUENCE {
|
||||
identifier TransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
causeInformation CHOICE {
|
||||
abortCause P-Abort-cause,
|
||||
userInformation UserAbortInformation
|
||||
} OPTIONAL
|
||||
}
|
||||
-- When the Abort package is generated by the Transaction sublayer,
|
||||
-- the P-Abort-cause must be present
|
||||
|
||||
P-Abort-cause ::= [PRIVATE 23] IMPLICIT INTEGER {
|
||||
unrecognizedPackageType(1),
|
||||
incorrectTransactionPortion(2),
|
||||
badlyStructuredTransactionPortion(3),
|
||||
unassignedRespondingTransactionID(4),
|
||||
permissionToReleaseProblem(5), -- for further study
|
||||
resourceUnavailable(6),
|
||||
unrecognizedDialoguePortionID(7),
|
||||
badlyStructuredDialoguePortion(8),
|
||||
missingDialoguePortion(9),
|
||||
inconsistentDialoguePortion(10)
|
||||
}
|
||||
|
||||
DialoguePortion ::= [PRIVATE 25] IMPLICIT SEQUENCE {
|
||||
version ProtocolVersion OPTIONAL,
|
||||
applicationContext CHOICE {
|
||||
integerApplicationId IntegerApplicationContext,
|
||||
objectApplicationId ObjectIDApplicationContext
|
||||
} OPTIONAL,
|
||||
userInformation UserInformation OPTIONAL,
|
||||
securityContext CHOICE {
|
||||
integerSecurityId [0] IMPLICIT INTEGER,
|
||||
objectSecurityId [1] IMPLICIT OBJECT IDENTIFIER
|
||||
} OPTIONAL,
|
||||
confidentiality [2] IMPLICIT Confidentiality OPTIONAL
|
||||
}
|
||||
|
||||
ProtocolVersion ::= [PRIVATE 26] IMPLICIT OCTET STRING(SIZE (1))
|
||||
-- 0000 0000 not used
|
||||
-- 0000 0001 T1.114-1996
|
||||
-- 0000 0010 T1.114-2000
|
||||
-- other reserved
|
||||
-- These values can be combined using the bit-wise logical or operation
|
||||
-- to indicate support for more than one version, e.g. the value 0000 0011
|
||||
-- means that both 1996 and 2000 versions are supported
|
||||
|
||||
IntegerApplicationContext ::= [PRIVATE 27] IMPLICIT INTEGER
|
||||
|
||||
ObjectIDApplicationContext ::= [PRIVATE 28] IMPLICIT OBJECT IDENTIFIER
|
||||
|
||||
UserInformation ::= [PRIVATE 29] IMPLICIT SEQUENCE OF EXTERNAL
|
||||
|
||||
Confidentiality ::= SEQUENCE {
|
||||
confidentialityId CHOICE {
|
||||
integerConfidentialityId [0] IMPLICIT INTEGER,
|
||||
objectConfidentialityId [1] IMPLICIT OBJECT IDENTIFIER
|
||||
} OPTIONAL,
|
||||
...
|
||||
-- The extension marker indicates the possible presence of items
|
||||
-- in the confidentiality set that are used by the confidentiality
|
||||
-- algorithm.
|
||||
}
|
||||
|
||||
UserAbortInformation ::= [PRIVATE 24] EXTERNAL
|
||||
|
||||
-- leave this portion undecoded
|
||||
ComponentSequence ::= TYPE-IDENTIFIER.&Type
|
||||
|
||||
END
|
|
@ -0,0 +1,75 @@
|
|||
DialoguePDUs {itu-t recommendation q 773 modules(2) dialoguePDUs(2) version1(1)
|
||||
} DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
EXPORTS dialogue-as-id, DialoguePDU;
|
||||
|
||||
-- abstract syntax name for structured dialogue APDUs
|
||||
dialogue-as-id OBJECT IDENTIFIER ::=
|
||||
{itu-t recommendation q 773 as(1) dialogue-as(1) version1(1)}
|
||||
|
||||
DialoguePDU ::= CHOICE {
|
||||
dialogueRequest AARQ-apdu,
|
||||
dialogueResponse AARE-apdu,
|
||||
dialogueAbort ABRT-apdu
|
||||
}
|
||||
|
||||
AARQ-apdu ::= [APPLICATION 0] IMPLICIT SEQUENCE {
|
||||
protocol-version
|
||||
[0] IMPLICIT BIT STRING {version1(0)} DEFAULT {version1},
|
||||
application-context-name [1] OBJECT IDENTIFIER,
|
||||
user-information [30] IMPLICIT SEQUENCE OF EXTERNAL OPTIONAL
|
||||
}
|
||||
|
||||
AARE-apdu ::= [APPLICATION 1] IMPLICIT SEQUENCE {
|
||||
protocol-version
|
||||
[0] IMPLICIT BIT STRING {version1(0)} DEFAULT {version1},
|
||||
application-context-name [1] OBJECT IDENTIFIER,
|
||||
result [2] Associate-result,
|
||||
result-source-diagnostic [3] Associate-source-diagnostic,
|
||||
user-information [30] IMPLICIT SEQUENCE OF EXTERNAL OPTIONAL
|
||||
}
|
||||
|
||||
-- RLRQ PDU is currently not used.
|
||||
-- It is included for completeness only.
|
||||
RLRQ-apdu ::= [APPLICATION 2] IMPLICIT SEQUENCE {
|
||||
reason [0] IMPLICIT Release-request-reason OPTIONAL,
|
||||
user-information [30] IMPLICIT SEQUENCE OF EXTERNAL OPTIONAL
|
||||
}
|
||||
|
||||
-- RLRE PDU is currently not used.
|
||||
-- It is included for completeness only
|
||||
RLRE-apdu ::= [APPLICATION 3] IMPLICIT SEQUENCE {
|
||||
reason [0] IMPLICIT Release-response-reason OPTIONAL,
|
||||
user-information [30] IMPLICIT SEQUENCE OF EXTERNAL OPTIONAL
|
||||
}
|
||||
|
||||
ABRT-apdu ::= [APPLICATION 4] IMPLICIT SEQUENCE {
|
||||
abort-source [0] IMPLICIT ABRT-source,
|
||||
user-information [30] IMPLICIT SEQUENCE OF EXTERNAL OPTIONAL
|
||||
}
|
||||
|
||||
ABRT-source ::= INTEGER {dialogue-service-user(0), dialogue-service-provider(1)
|
||||
}
|
||||
|
||||
Associate-result ::= INTEGER {accepted(0), reject-permanent(1)}
|
||||
|
||||
Associate-source-diagnostic ::= CHOICE {
|
||||
dialogue-service-user
|
||||
[1] INTEGER {null(0), no-reason-given(1),
|
||||
application-context-name-not-supported(2)},
|
||||
dialogue-service-provider
|
||||
[2] INTEGER {null(0), no-reason-given(1), no-common-dialogue-portion(2)}
|
||||
}
|
||||
|
||||
-- Release-request-reason is currently not used.
|
||||
-- It is included for completeness only.
|
||||
Release-request-reason ::= INTEGER {normal(0), urgent(1), user-defined(30)
|
||||
}
|
||||
|
||||
-- Release-response-reason is currently not used.
|
||||
-- It is included for completeness only.
|
||||
Release-response-reason ::= INTEGER {
|
||||
normal(0), not-finished(1), user-defined(30)}
|
||||
|
||||
END -- DialoguePDUs
|
|
@ -0,0 +1,77 @@
|
|||
EBIN = ../../ebin/itu
|
||||
|
||||
ERLC = erlc
|
||||
ERLCFLAGS = -b beam -W -v +warn_unused_vars -o $(EBIN)
|
||||
|
||||
ASNC = $(ERLC)
|
||||
ASNCFLAGS = -W -b ber_bin +optimize +driver
|
||||
|
||||
$(EBIN)/%.beam:%.erl
|
||||
$(ERLC) $(ERLCFLAGS) $<
|
||||
|
||||
.SECONDARY: %.erl
|
||||
%.erl:%.asn
|
||||
$(ASNC) $(ASNCFLAGS) $<
|
||||
|
||||
.PHONY: default
|
||||
default: all
|
||||
|
||||
.PHONY: all
|
||||
all: q773 q755 q775
|
||||
|
||||
.PHONY: q773
|
||||
q773: $(EBIN)/TR.beam $(EBIN)/TCAPMessages.beam \
|
||||
$(EBIN)/DialoguePDUs.beam $(EBIN)/UnidialoguePDUs.beam
|
||||
|
||||
TR.erl: TCAPMessages.erl
|
||||
TCAPMessages.erl: Remote-Operations-Useful-Definitions.erl
|
||||
|
||||
.PHONY: q755
|
||||
q755: $(EBIN)/TC-Testing-User.beam
|
||||
|
||||
TC-Testing-User.erl: TC-TMP.erl Remote-Operations-Information-Objects.erl
|
||||
|
||||
.PHONY: q775
|
||||
q775: $(EBIN)/TCAP-Examples.beam $(EBIN)/TCAP-Tools.beam
|
||||
|
||||
TCAP-Examples.erl: Remote-Operations-Information-Objects.erl
|
||||
TCAP-Tools.erl: Remote-Operations-Information-Objects.erl
|
||||
TC-Notation-Extensions.erl: TCAPMessages.erl \
|
||||
Remote-Operations-Useful-Definitions.erl \
|
||||
Remote-Operations-Information-Objects.erl \
|
||||
Remote-Operations-Generic-ROS-PDUs.erl \
|
||||
UnidialoguePDUs.erl DialoguePDUs.erl
|
||||
|
||||
Remote-Operations-Useful-Definitions.erl: \
|
||||
Remote-Operations-Information-Objects.asn \
|
||||
Remote-Operations-Generic-ROS-PDUs.asn \
|
||||
Remote-Operations-Useful-Definitions.asn
|
||||
$(ASNC) $(ASNCFLAGS) $^
|
||||
|
||||
Remote-Operations-Information-Objects.erl: \
|
||||
Remote-Operations-Information-Objects.asn \
|
||||
Remote-Operations-Generic-ROS-PDUs.asn \
|
||||
Remote-Operations-Useful-Definitions.asn
|
||||
$(ASNC) $(ASNCFLAGS) $^
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
- rm -f *.asn1db
|
||||
- rm -f TR.erl TCAPMessages.erl \
|
||||
DialoguePDUs.erl UnidialoguePDUs.erl \
|
||||
Remote-Operations-Useful-Definitions.erl \
|
||||
Remote-Operations-Information-Objects.erl \
|
||||
Remote-Operations-Generic-ROS-PDUs.erl \
|
||||
TC-Testing-User.erl TC-TMP.erl \
|
||||
TCAP-Examples.erl TCAP-Tools.erl \
|
||||
TC-Notation-Extensions.erl
|
||||
- rm -f Remote-Operations-Generic-ROS-PDUs.hrl \
|
||||
DialoguePDUs.hrl UnidialoguePDUs.hrl \
|
||||
TCAPMessages.hrl TR.hrl \
|
||||
TC-Testing-User.hrl TC-TMP.hrl \
|
||||
TCAP-Examples.hrl
|
||||
- rm -f $(EBIN)/TR.beam $(EBIN)/TCAPMessages.beam \
|
||||
$(EBIN)/DialoguePDUs.beam $(EBIN)/UnidialoguePDUs.beam \
|
||||
$(EBIN)/TC-Testing-User.beam \
|
||||
$(EBIN)/TCAP-Examples.beam $(EBIN)/TCAP-Tools.beam
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
-- Module Remote-Operations-Generic-ROS-PDUs (Rec. X.880:07/1994)
|
||||
Remote-Operations-Generic-ROS-PDUs {joint-iso-itu-t remote-operations(4)
|
||||
generic-ROS-PDUs(6) version1(0)} DEFINITIONS IMPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
-- exports everything
|
||||
IMPORTS
|
||||
OPERATION, ERROR
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)};
|
||||
|
||||
ROS{InvokeId:InvokeIdSet, OPERATION:Invokable, OPERATION:Returnable} ::=
|
||||
CHOICE {
|
||||
invoke [1] Invoke{{InvokeIdSet}, {Invokable}},
|
||||
returnResult [2] ReturnResult{{Returnable}},
|
||||
returnError [3] ReturnError{{Errors {{Returnable}}}},
|
||||
reject [4] Reject
|
||||
}
|
||||
(CONSTRAINED BY { -- must conform to the above definition --} !
|
||||
RejectProblem:general-unrecognizedPDU)
|
||||
|
||||
Invoke{InvokeId:InvokeIdSet, OPERATION:Operations} ::= SEQUENCE {
|
||||
invokeId
|
||||
InvokeId(InvokeIdSet)
|
||||
(CONSTRAINED BY { -- must be unambiguous --} !
|
||||
RejectProblem:invoke-duplicateInvocation),
|
||||
linkedId
|
||||
CHOICE {present [0] IMPLICIT present < InvokeId,
|
||||
absent [1] IMPLICIT NULL
|
||||
}
|
||||
(CONSTRAINED BY { -- must identify an outstanding operation --} !
|
||||
RejectProblem:invoke-unrecognizedLinkedId)
|
||||
(CONSTRAINED BY { -- which has one or more linked operations--} !
|
||||
RejectProblem:invoke-linkedResponseUnexpected) OPTIONAL,
|
||||
opcode
|
||||
OPERATION.&operationCode
|
||||
({Operations} !RejectProblem:invoke-unrecognizedOperation),
|
||||
argument
|
||||
OPERATION.&ArgumentType
|
||||
({Operations}{@opcode} !RejectProblem:invoke-mistypedArgument) OPTIONAL
|
||||
}
|
||||
(CONSTRAINED BY { -- must conform to the above definition --} !
|
||||
RejectProblem:general-mistypedPDU)
|
||||
(WITH COMPONENTS {
|
||||
...,
|
||||
linkedId ABSENT
|
||||
} |
|
||||
WITH COMPONENTS {
|
||||
...,
|
||||
linkedId PRESENT,
|
||||
opcode (CONSTRAINED BY {
|
||||
-- must be in the &Linked field of the associated operation --}
|
||||
!RejectProblem:invoke-unexpectedLinkedOperation)
|
||||
})
|
||||
|
||||
-- continued on the next page
|
||||
ReturnResult{OPERATION:Operations} ::= SEQUENCE {
|
||||
invokeId
|
||||
InvokeId
|
||||
(CONSTRAINED BY { -- must be that for an outstanding operation --} !
|
||||
RejectProblem:returnResult-unrecognizedInvocation)
|
||||
(CONSTRAINED BY { -- which returns a result --} !
|
||||
RejectProblem:returnResult-resultResponseUnexpected),
|
||||
result
|
||||
SEQUENCE {opcode
|
||||
OPERATION.&operationCode({Operations})
|
||||
(CONSTRAINED BY { -- identified by invokeId --} !
|
||||
RejectProblem:returnResult-unrecognizedInvocation),
|
||||
result
|
||||
OPERATION.&ResultType
|
||||
({Operations}{@.opcode} !
|
||||
RejectProblem:returnResult-mistypedResult)} OPTIONAL
|
||||
}
|
||||
(CONSTRAINED BY { -- must conform to the above definition --} !
|
||||
RejectProblem:general-mistypedPDU)
|
||||
|
||||
ReturnError{ERROR:Errors} ::= SEQUENCE {
|
||||
invokeId
|
||||
InvokeId
|
||||
(CONSTRAINED BY { -- must be that for an outstanding operation --} !
|
||||
RejectProblem:returnError-unrecognizedInvocation)
|
||||
(CONSTRAINED BY { -- which returns an error --} !
|
||||
RejectProblem:returnError-errorResponseUnexpected),
|
||||
errcode
|
||||
ERROR.&errorCode({Errors} !RejectProblem:returnError-unrecognizedError)
|
||||
(CONSTRAINED BY {
|
||||
-- must be in the &Errors field of the associated operation --} !
|
||||
RejectProblem:returnError-unexpectedError),
|
||||
parameter
|
||||
ERROR.&ParameterType
|
||||
({Errors}{@errcode} !RejectProblem:returnError-mistypedParameter)
|
||||
OPTIONAL
|
||||
}
|
||||
(CONSTRAINED BY { -- must conform to the above definition --} !
|
||||
RejectProblem:general-mistypedPDU)
|
||||
|
||||
Reject ::= SEQUENCE {
|
||||
invokeId InvokeId,
|
||||
problem
|
||||
CHOICE {general [0] GeneralProblem,
|
||||
invoke [1] InvokeProblem,
|
||||
returnResult [2] ReturnResultProblem,
|
||||
returnError [3] ReturnErrorProblem}
|
||||
}
|
||||
(CONSTRAINED BY { -- must conform to the above definition --} !
|
||||
RejectProblem:general-mistypedPDU)
|
||||
|
||||
GeneralProblem ::= INTEGER {
|
||||
unrecognizedPDU(0), mistypedPDU(1), badlyStructuredPDU(2)}
|
||||
|
||||
-- continued on the next page
|
||||
InvokeProblem ::= INTEGER {
|
||||
duplicateInvocation(0), unrecognizedOperation(1), mistypedArgument(2),
|
||||
resourceLimitation(3), releaseInProgress(4), unrecognizedLinkedId(5),
|
||||
linkedResponseUnexpected(6), unexpectedLinkedOperation(7)}
|
||||
|
||||
ReturnResultProblem ::= INTEGER {
|
||||
unrecognizedInvocation(0), resultResponseUnexpected(1), mistypedResult(2)
|
||||
}
|
||||
|
||||
ReturnErrorProblem ::= INTEGER {
|
||||
unrecognizedInvocation(0), errorResponseUnexpected(1), unrecognizedError(2),
|
||||
unexpectedError(3), mistypedParameter(4)}
|
||||
|
||||
RejectProblem ::= INTEGER {
|
||||
general-unrecognizedPDU(0), general-mistypedPDU(1),
|
||||
general-badlyStructuredPDU(2), invoke-duplicateInvocation(10),
|
||||
invoke-unrecognizedOperation(11), invoke-mistypedArgument(12),
|
||||
invoke-resourceLimitation(13), invoke-releaseInProgress(14),
|
||||
invoke-unrecognizedLinkedId(15), invoke-linkedResponseUnexpected(16),
|
||||
invoke-unexpectedLinkedOperation(17),
|
||||
returnResult-unrecognizedInvocation(20),
|
||||
returnResult-resultResponseUnexpected(21), returnResult-mistypedResult(22),
|
||||
returnError-unrecognizedInvocation(30),
|
||||
returnError-errorResponseUnexpected(31), returnError-unrecognizedError(32),
|
||||
returnError-unexpectedError(33), returnError-mistypedParameter(34)}
|
||||
|
||||
InvokeId ::= CHOICE {present INTEGER,
|
||||
absent NULL
|
||||
}
|
||||
|
||||
noInvokeId InvokeId ::= absent:NULL
|
||||
|
||||
NoInvokeId InvokeId ::= {noInvokeId}
|
||||
|
||||
Errors{OPERATION:Operations} ERROR ::= {Operations.&Errors}
|
||||
|
||||
-- continued on the next page
|
||||
Bind{OPERATION:operation} ::= CHOICE {
|
||||
bind-invoke [16] OPERATION.&ArgumentType({operation}),
|
||||
bind-result [17] OPERATION.&ResultType({operation}),
|
||||
bind-error [18] OPERATION.&Errors.&ParameterType({operation})
|
||||
}
|
||||
|
||||
Unbind{OPERATION:operation} ::= CHOICE {
|
||||
unbind-invoke [19] OPERATION.&ArgumentType({operation}),
|
||||
unbind-result [20] OPERATION.&ResultType({operation}),
|
||||
unbind-error [21] OPERATION.&Errors.&ParameterType({operation})
|
||||
}
|
||||
|
||||
END -- end of generic ROS PDU definitions
|
|
@ -0,0 +1,113 @@
|
|||
-- Module Remote-Operations-Information-Objects (Rec. X.880:07/1994)
|
||||
Remote-Operations-Information-Objects {joint-iso-itu-t remote-operations(4)
|
||||
informationObjects(5) version1(0)} DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
-- exports everything
|
||||
IMPORTS
|
||||
emptyBind, emptyUnbind
|
||||
FROM Remote-Operations-Useful-Definitions {joint-iso-itu-t
|
||||
remote-operations(4) useful-definitions(7) version1(0)};
|
||||
|
||||
OPERATION ::= CLASS {
|
||||
&ArgumentType OPTIONAL,
|
||||
&argumentTypeOptional BOOLEAN OPTIONAL,
|
||||
&returnResult BOOLEAN DEFAULT TRUE,
|
||||
&ResultType OPTIONAL,
|
||||
&resultTypeOptional BOOLEAN OPTIONAL,
|
||||
&Errors ERROR OPTIONAL,
|
||||
&Linked OPERATION OPTIONAL,
|
||||
&synchronous BOOLEAN DEFAULT FALSE,
|
||||
&alwaysReturns BOOLEAN DEFAULT TRUE,
|
||||
&InvokePriority Priority OPTIONAL,
|
||||
&ResultPriority Priority OPTIONAL,
|
||||
&operationCode Code UNIQUE OPTIONAL
|
||||
}
|
||||
WITH SYNTAX {
|
||||
[ARGUMENT &ArgumentType [OPTIONAL &argumentTypeOptional]]
|
||||
[RESULT &ResultType [OPTIONAL &resultTypeOptional]]
|
||||
[RETURN RESULT &returnResult]
|
||||
[ERRORS &Errors]
|
||||
[LINKED &Linked]
|
||||
[SYNCHRONOUS &synchronous]
|
||||
[ALWAYS RESPONDS &alwaysReturns]
|
||||
[INVOKE PRIORITY &InvokePriority]
|
||||
[RESULT-PRIORITY &ResultPriority]
|
||||
[CODE &operationCode]
|
||||
}
|
||||
|
||||
ERROR ::= CLASS {
|
||||
&ParameterType OPTIONAL,
|
||||
¶meterTypeOptional BOOLEAN OPTIONAL,
|
||||
&ErrorPriority Priority OPTIONAL,
|
||||
&errorCode Code UNIQUE OPTIONAL
|
||||
}
|
||||
WITH SYNTAX {
|
||||
[PARAMETER &ParameterType [OPTIONAL ¶meterTypeOptional]]
|
||||
[PRIORITY &ErrorPriority]
|
||||
[CODE &errorCode]
|
||||
}
|
||||
|
||||
OPERATION-PACKAGE ::= CLASS {
|
||||
&Both OPERATION OPTIONAL,
|
||||
&Consumer OPERATION OPTIONAL,
|
||||
&Supplier OPERATION OPTIONAL,
|
||||
&id OBJECT IDENTIFIER UNIQUE OPTIONAL
|
||||
}
|
||||
WITH SYNTAX {
|
||||
[OPERATIONS &Both]
|
||||
[CONSUMER INVOKES &Supplier]
|
||||
[SUPPLIER INVOKES &Consumer]
|
||||
[ID &id]
|
||||
}
|
||||
|
||||
CONNECTION-PACKAGE ::= CLASS {
|
||||
&bind OPERATION DEFAULT emptyBind,
|
||||
&unbind OPERATION DEFAULT emptyUnbind,
|
||||
&responderCanUnbind BOOLEAN DEFAULT FALSE,
|
||||
&unbindCanFail BOOLEAN DEFAULT FALSE,
|
||||
&id OBJECT IDENTIFIER UNIQUE OPTIONAL
|
||||
}
|
||||
WITH SYNTAX {
|
||||
[BIND &bind]
|
||||
[UNBIND &unbind]
|
||||
[RESPONDER UNBIND &responderCanUnbind]
|
||||
[FAILURE TO UNBIND &unbindCanFail]
|
||||
[ID &id]
|
||||
}
|
||||
|
||||
CONTRACT ::= CLASS {
|
||||
&connection CONNECTION-PACKAGE OPTIONAL,
|
||||
&OperationsOf OPERATION-PACKAGE OPTIONAL,
|
||||
&InitiatorConsumerOf OPERATION-PACKAGE OPTIONAL,
|
||||
&InitiatorSupplierOf OPERATION-PACKAGE OPTIONAL,
|
||||
&id OBJECT IDENTIFIER UNIQUE OPTIONAL
|
||||
}
|
||||
WITH SYNTAX {
|
||||
[CONNECTION &connection]
|
||||
[OPERATIONS OF &OperationsOf]
|
||||
[INITIATOR CONSUMER OF &InitiatorConsumerOf]
|
||||
[RESPONDER CONSUMER OF &InitiatorSupplierOf]
|
||||
[ID &id]
|
||||
}
|
||||
|
||||
ROS-OBJECT-CLASS ::= CLASS {
|
||||
&Is ROS-OBJECT-CLASS OPTIONAL,
|
||||
&Initiates CONTRACT OPTIONAL,
|
||||
&Responds CONTRACT OPTIONAL,
|
||||
&InitiatesAndResponds CONTRACT OPTIONAL,
|
||||
&id OBJECT IDENTIFIER UNIQUE
|
||||
}
|
||||
WITH SYNTAX {
|
||||
[IS &Is]
|
||||
[BOTH &InitiatesAndResponds]
|
||||
[INITIATES &Initiates]
|
||||
[RESPONDS &Responds]
|
||||
ID &id
|
||||
}
|
||||
|
||||
Code ::= CHOICE {local INTEGER, global OBJECT IDENTIFIER}
|
||||
|
||||
Priority ::= INTEGER(0..MAX)
|
||||
|
||||
END -- end of Information Object specifications
|
|
@ -0,0 +1,91 @@
|
|||
-- Module Remote-Operations-Useful-Definitions (Rec. X.880:07/1994)
|
||||
Remote-Operations-Useful-Definitions {joint-iso-itu-t remote-operations(4)
|
||||
useful-definitions(7) version1(0)} DEFINITIONS IMPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
-- exports everything
|
||||
IMPORTS
|
||||
OPERATION, ERROR, OPERATION-PACKAGE, Code
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)}
|
||||
InvokeId, ROS{}
|
||||
FROM Remote-Operations-Generic-ROS-PDUs {joint-iso-itu-t
|
||||
remote-operations(4) generic-ROS-PDUs(6) version1(0)};
|
||||
|
||||
emptyBind OPERATION ::= {ERRORS {refuse}
|
||||
SYNCHRONOUS TRUE
|
||||
}
|
||||
|
||||
emptyUnbind OPERATION ::= {SYNCHRONOUS TRUE
|
||||
}
|
||||
|
||||
refuse ERROR ::= {CODE local:-1
|
||||
}
|
||||
|
||||
no-op OPERATION ::= {ALWAYS RESPONDS FALSE
|
||||
CODE local:-1
|
||||
}
|
||||
|
||||
Forward{OPERATION:OperationSet} OPERATION ::=
|
||||
{OperationSet | OperationSet.&Linked.&Linked |
|
||||
OperationSet.&Linked.&Linked.&Linked.&Linked}
|
||||
|
||||
Reverse{OPERATION:OperationSet} OPERATION ::= {Forward{{OperationSet.&Linked}}}
|
||||
|
||||
ConsumerPerforms{OPERATION-PACKAGE:package} OPERATION ::=
|
||||
{Forward{{package.&Consumer}} | Forward{{package.&Both}} |
|
||||
Reverse{{package.&Supplier}} | Reverse{{package.&Both}}}
|
||||
|
||||
SupplierPerforms{OPERATION-PACKAGE:package} OPERATION ::=
|
||||
{Forward{{package.&Supplier}} | Forward{{package.&Both}} |
|
||||
Reverse{{package.&Consumer}} | Reverse{{package.&Both}}}
|
||||
|
||||
AllOperations{OPERATION-PACKAGE:package} OPERATION ::=
|
||||
{ConsumerPerforms{package} | SupplierPerforms{package}}
|
||||
|
||||
-- continued on the next page
|
||||
recode{OPERATION:operation, Code:code} OPERATION ::= {
|
||||
ARGUMENT operation.&ArgumentType
|
||||
OPTIONAL operation.&argumentTypeOptional
|
||||
RESULT operation.&ResultType
|
||||
OPTIONAL operation.&resultTypeOptional
|
||||
RETURN RESULT operation.&returnResult
|
||||
ERRORS {operation.&Errors}
|
||||
LINKED {operation.&Linked}
|
||||
SYNCHRONOUS operation.&synchronous
|
||||
ALWAYS RESPONDS operation.&alwaysReturns
|
||||
INVOKE PRIORITY {operation.&InvokePriority}
|
||||
RESULT-PRIORITY {operation.&ResultPriority}
|
||||
CODE code
|
||||
}
|
||||
|
||||
switch{OPERATION-PACKAGE:package, OBJECT IDENTIFIER:id} OPERATION-PACKAGE ::=
|
||||
{
|
||||
OPERATIONS {package.&Both}
|
||||
CONSUMER INVOKES {package.&Consumer}
|
||||
SUPPLIER INVOKES {package.&Supplier}
|
||||
ID id
|
||||
}
|
||||
|
||||
combine{OPERATION-PACKAGE:ConsumerConsumes, OPERATION-PACKAGE:ConsumerSupplies,
|
||||
OPERATION-PACKAGE:base} OPERATION-PACKAGE ::= {
|
||||
OPERATIONS {ConsumerConsumes.&Both | ConsumerSupplies.&Both}
|
||||
CONSUMER INVOKES {ConsumerConsumes.&Consumer | ConsumerSupplies.&Supplier}
|
||||
SUPPLIER INVOKES {ConsumerConsumes.&Supplier | ConsumerSupplies.&Consumer}
|
||||
ID base.&id
|
||||
}
|
||||
|
||||
ROS-SingleAS{InvokeId:InvokeIdSet, OPERATION-PACKAGE:package} ::=
|
||||
ROS{{InvokeIdSet}, {AllOperations {package}}, {AllOperations {package}}}
|
||||
|
||||
ROS-ConsumerAS{InvokeId:InvokeIdSet, OPERATION-PACKAGE:package} ::=
|
||||
ROS
|
||||
{{InvokeIdSet}, {ConsumerPerforms {package}},
|
||||
{SupplierPerforms {package}}}
|
||||
|
||||
ROS-SupplierAS{InvokeId:InvokeIdSet, OPERATION-PACKAGE:package} ::=
|
||||
ROS
|
||||
{{InvokeIdSet}, {SupplierPerforms {package}},
|
||||
{ConsumerPerforms {package}}}
|
||||
|
||||
END -- end of useful definitions.
|
|
@ -0,0 +1,84 @@
|
|||
-- Module TC-Notation-Extensions (Q.775:06/1997)
|
||||
|
||||
TC-Notation-Extensions {itu-t recommendation q 775 modules(2)
|
||||
notation-extension(4) version1(1)} DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
IMPORTS
|
||||
TCMessage{}
|
||||
FROM TCAPMessages {itu-t recommendation q 773 modules(2) messages(1)
|
||||
version3(3)}
|
||||
Bind{}, Unbind{}
|
||||
FROM Remote-Operations-Generic-ROS-PDUs {joint-iso-itu-t
|
||||
remote-operations(4) generic-ROS-PDUs(6) version1(0)}
|
||||
AllOperations{}, ConsumerPerforms{}, SupplierPerforms{}, combine{}
|
||||
FROM Remote-Operations-Useful-Definitions {joint-iso-itu-t
|
||||
remote-operations(4) useful-definitions(7) version1(0)}
|
||||
CONTRACT, OPERATION-PACKAGE
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)}
|
||||
UniDialoguePDU, uniDialogue-as-id
|
||||
FROM UnidialoguePDUs {itu-t recommendation q 773 modules(2)
|
||||
unidialoguePDUs(3) version1(1)}
|
||||
DialoguePDU, dialogue-as-id
|
||||
FROM DialoguePDUs {itu-t recommendation q 773 modules(2) dialoguePDUs(2)
|
||||
version1(1)};
|
||||
|
||||
APPLICATION-CONTEXT ::= CLASS {
|
||||
&associationContract CONTRACT,
|
||||
&dialogueMode DialogueMode,
|
||||
&termination Termination OPTIONAL,
|
||||
&componentGrouping BOOLEAN DEFAULT TRUE,
|
||||
&dialogueAndComponentGrouping BOOLEAN DEFAULT TRUE,
|
||||
&AdditionalASEs OBJECT IDENTIFIER OPTIONAL,
|
||||
&AbstractSyntaxes ABSTRACT-SYNTAX,
|
||||
&applicationContextName OBJECT IDENTIFIER UNIQUE
|
||||
}
|
||||
WITH SYNTAX {
|
||||
CONTRACT &associationContract
|
||||
DIALOGUE MODE &dialogueMode
|
||||
[TERMINATION &termination]
|
||||
[COMPONENT GROUPING ALLOWED &componentGrouping]
|
||||
[DIALOGUE WITH COMPONENTS ALLOWED &dialogueAndComponentGrouping]
|
||||
[ADDITIONAL ASES &AdditionalASEs]
|
||||
ABSTRACT SYNTAXES &AbstractSyntaxes
|
||||
APPLICATION CONTEXT NAME &applicationContextName
|
||||
}
|
||||
|
||||
DialogueMode ::= ENUMERATED {structured(1), unstructured(2)}
|
||||
|
||||
Termination ::= ENUMERATED {basic(1), prearranged(2)}
|
||||
|
||||
dialogue-abstract-syntax ABSTRACT-SYNTAX ::= {
|
||||
DialoguePDU
|
||||
IDENTIFIED BY dialogue-as-id
|
||||
}
|
||||
|
||||
uniDialogue-abstract-syntax ABSTRACT-SYNTAX ::= {
|
||||
UniDialoguePDU
|
||||
IDENTIFIED BY uniDialogue-as-id
|
||||
}
|
||||
|
||||
TCSingleAS{OPERATION-PACKAGE:package} ::=
|
||||
TCMessage{{AllOperations {package}}, {AllOperations {package}}}
|
||||
|
||||
TCConsumerAS{OPERATION-PACKAGE:package} ::=
|
||||
TCMessage{{ConsumerPerforms {package}}, {ConsumerPerforms {package}}}
|
||||
|
||||
TCSupplierAS{OPERATION-PACKAGE:package} ::=
|
||||
TCMessage{{SupplierPerforms {package}}, {SupplierPerforms {package}}}
|
||||
|
||||
AllPackagesAS{APPLICATION-CONTEXT:ac} ::=
|
||||
TCSingleAS
|
||||
{combine{{ac.&associationContract.&OperationsOf |
|
||||
ac.&associationContract.&InitiatorConsumerOf |
|
||||
ac.&associationContract.&InitiatorSupplierOf},
|
||||
{}, --was illegaly empty, needs to be changed
|
||||
{-- Information Object of class OPERATION-PACKAGE to be defined --}}}
|
||||
|
||||
ConnectionAS{APPLICATION-CONTEXT:ac} ::= CHOICE {
|
||||
bind Bind{ac.&associationContract.&connection.&bind},
|
||||
unbind Unbind{ac.&associationContract.&connection.&unbind}
|
||||
}
|
||||
|
||||
END
|
|
@ -0,0 +1,56 @@
|
|||
-- Module TC-TMP (Q.755.2:09/1997)
|
||||
TC-TMP {itu-t recommendation q 755 modules(0) tmp(2) version1(1)} DEFINITIONS
|
||||
IMPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
TMP-PDU ::= CHOICE {
|
||||
testInit [0] TestInit,
|
||||
testContinue [1] CommandSequence,
|
||||
testDataEcho [2] UserData
|
||||
}
|
||||
|
||||
CommandSequence ::= SEQUENCE SIZE (0..maxNbOfCommands) OF TestCommand
|
||||
|
||||
maxNbOfCommands INTEGER ::= 30
|
||||
|
||||
TestInit ::= SEQUENCE {
|
||||
timeout INTEGER(1..127) OPTIONAL, -- T-Test (unit is 30 sec)
|
||||
commands CommandSequence,
|
||||
...
|
||||
}
|
||||
|
||||
UserData ::= CHOICE {
|
||||
simple OCTET STRING(SIZE (0..maxUserDataLength)),
|
||||
complex [0] ABSTRACT-SYNTAX.&Type
|
||||
}
|
||||
|
||||
maxUserDataLength INTEGER ::= 2048
|
||||
|
||||
TestCommand ::= CHOICE {wait [0] DialogueReference,
|
||||
action [1] ActionInfo
|
||||
}
|
||||
|
||||
DialogueReference ::= CHOICE {unspecified NULL,
|
||||
dialogue INTEGER(0..255)
|
||||
}
|
||||
|
||||
ActionInfo ::= SEQUENCE {
|
||||
service ServiceType,
|
||||
dialogueReference DialogueReference DEFAULT unspecified:NULL,
|
||||
to-be-echoed UserData OPTIONAL,
|
||||
...
|
||||
}
|
||||
|
||||
ServiceType ::= ENUMERATED {
|
||||
v1988uniReq(10), v1993uniReq(11), v1988beginReq(12), v1993beginReq(13),
|
||||
continueReq(14), basicEndReq(15), localEndReq(16), uAbortReq(17),
|
||||
class1invokeReq(21), class2invokeReq(22), class3invokeReq(23),
|
||||
class4invokeReq(24), linkedInvokeReq(25), resultNlReq(26), resultLReq(27),
|
||||
uErrorReq(28), uCancelReq(29), uRejectReq(30), ...
|
||||
}
|
||||
|
||||
-- abstract syntax name for TMP-PDUs
|
||||
tmp-pdus-as OBJECT IDENTIFIER ::=
|
||||
{itu-t recommendation q 755 as(4) tmp-pdus(1) version1(1)}
|
||||
|
||||
END
|
|
@ -0,0 +1,89 @@
|
|||
-- Module TC-Testing-User (Q.755.2:09/1997)
|
||||
TC-Testing-User {itu-t recommendation q 755 modules(0) testing-user(1)
|
||||
version1(1)} DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
IMPORTS
|
||||
-- OPERATION,
|
||||
-- ERROR
|
||||
--FROM TCAPMessages {itu-t recommendation q 773 modules(2) messages(1) version2(2)}
|
||||
-- APPLICATION-SERVICE-ELEMENT
|
||||
--FROM Notation-Extensions {joint-iso-itu-t remote-operations(4) notation-extension(2)}
|
||||
TMP-PDU
|
||||
FROM TC-TMP {itu-t recommendation q 755 modules(0) tmp(2) version1(1)}
|
||||
OPERATION-PACKAGE, Code, OPERATION, ERROR
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)};
|
||||
|
||||
-- application-context-names
|
||||
ac-id OBJECT IDENTIFIER ::=
|
||||
{itu-t recommendation q 755 ac(5)}
|
||||
|
||||
testing-ac-id OBJECT IDENTIFIER ::= {ac-id testing-ac(1) version1(1)}
|
||||
|
||||
-- ase
|
||||
testing-User-ASE OPERATION-PACKAGE ::= {
|
||||
CONSUMER INVOKES {localConsumerOperation | globalConsumerOperation}
|
||||
-- consumer is the test system
|
||||
SUPPLIER INVOKES
|
||||
{class1SupplierOperation | class2SupplierOperation |
|
||||
class3SupplierOperation | class4SupplierOperation |
|
||||
globalSupplierOperation}
|
||||
-- supplier is the test responder
|
||||
ID testing-ac-id
|
||||
}
|
||||
|
||||
localConsumerOperation OPERATION ::= {
|
||||
ARGUMENT TMP-PDU RESULT TMP-PDU ERRORS {localSupplierError}
|
||||
CODE local:0
|
||||
}
|
||||
|
||||
globalConsumerOperation OPERATION ::= {
|
||||
ARGUMENT TMP-PDU RESULT TMP-PDU ERRORS {globalSupplierError}
|
||||
CODE
|
||||
global:{itu-t recommendation q 755 operations(1) consumer(1)}
|
||||
}
|
||||
|
||||
class1SupplierOperation OPERATION ::= {
|
||||
ARGUMENT TMP-PDU RESULT TMP-PDU ERRORS {localConsumerError}
|
||||
LINKED {localConsumerOperation}
|
||||
CODE local:1
|
||||
}
|
||||
|
||||
class2SupplierOperation OPERATION ::= {
|
||||
ARGUMENT TMP-PDU ERRORS {localConsumerError}
|
||||
LINKED {localConsumerOperation}
|
||||
CODE local:2
|
||||
}
|
||||
|
||||
class3SupplierOperation OPERATION ::= {
|
||||
ARGUMENT TMP-PDU RESULT TMP-PDU CODE local:3
|
||||
}
|
||||
|
||||
class4SupplierOperation OPERATION ::= {ARGUMENT TMP-PDU CODE local:4
|
||||
}
|
||||
|
||||
globalSupplierOperation OPERATION ::= {
|
||||
ARGUMENT TMP-PDU RESULT TMP-PDU ERRORS {globalConsumerError}
|
||||
LINKED {globalConsumerOperation}
|
||||
CODE
|
||||
global:{itu-t recommendation q 755 operations(1) supplier(2)}
|
||||
}
|
||||
|
||||
localConsumerError ERROR ::= {PARAMETER TMP-PDU CODE local:1
|
||||
}
|
||||
|
||||
globalConsumerError ERROR ::= {
|
||||
PARAMETER TMP-PDU CODE
|
||||
global:{itu-t recommendation q 755 errors(2) consumer(1)}
|
||||
}
|
||||
|
||||
localSupplierError ERROR ::= {PARAMETER TMP-PDU CODE local:2
|
||||
}
|
||||
|
||||
globalSupplierError ERROR ::= {
|
||||
PARAMETER TMP-PDU CODE
|
||||
global:{itu-t recommendation q 755 errors(2) supplier(2)}
|
||||
}
|
||||
|
||||
END
|
|
@ -0,0 +1,71 @@
|
|||
-- Module TCAP-Examples (Q.775:06/1997)
|
||||
|
||||
TCAP-Examples {itu-t recommendation q 775 modules(2) examples(2) version1(1)}
|
||||
DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
IMPORTS
|
||||
OPERATION, ERROR
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)};
|
||||
|
||||
provideRoutingInformation OPERATION ::= {
|
||||
ARGUMENT RequestArgument
|
||||
RESULT RoutingInformation
|
||||
ERRORS
|
||||
{invalidCalledNumber | subscriberNotReachable | calledBarred |
|
||||
processingFailure}
|
||||
LINKED {getCallingPartyAddress}
|
||||
}
|
||||
|
||||
-- timer T-pi = 10 s
|
||||
getCallingPartyAddress OPERATION ::= {
|
||||
RESULT CallingPartyAddress
|
||||
ERRORS {callingPartyAddressNotAvailable | processingFailure}
|
||||
CODE local:0
|
||||
}
|
||||
|
||||
-- timer T-gp = 5 s
|
||||
invalidCalledNumber ERROR ::= {CODE local:1
|
||||
}
|
||||
|
||||
subscriberNotReachable ERROR ::= {CODE local:2
|
||||
}
|
||||
|
||||
calledBarred ERROR ::= {CODE local:3
|
||||
}
|
||||
|
||||
callingPartyAddressNotAvailable ERROR ::= {CODE local:4
|
||||
}
|
||||
|
||||
processingFailure ERROR ::= {CODE local:5
|
||||
}
|
||||
|
||||
-- data types
|
||||
RequestArgument ::= SEQUENCE {
|
||||
calledNumber IsdnNumber,
|
||||
basicService BasicServiceIndicator OPTIONAL
|
||||
}
|
||||
|
||||
RoutingInformation ::= CHOICE {
|
||||
reroutingNumber [0] IMPLICIT IsdnNumber,
|
||||
forwardedToNumber [1] IMPLICIT IsdnNumber
|
||||
}
|
||||
|
||||
BasicServiceIndicator ::= ENUMERATED {speech(0), unrestrictedDigital(1)}
|
||||
|
||||
CallingPartyAddress ::= IsdnNumber
|
||||
|
||||
IsdnNumber ::= SEQUENCE {
|
||||
typeOfAddress TypeOfAddress,
|
||||
digits TelephonyString
|
||||
}
|
||||
|
||||
TypeOfAddress ::= ENUMERATED {national(0), international(1), private(2)}
|
||||
|
||||
TelephonyString ::=
|
||||
IA5String
|
||||
(FROM ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "*" |
|
||||
"#"))(SIZE (1..15))
|
||||
|
||||
END
|
|
@ -0,0 +1,43 @@
|
|||
-- Module TCAP-Tools (Q.775:06/1997)
|
||||
|
||||
TCAP-Tools {itu-t recommendation q 775 modules(2) tools(1) version1(1)}
|
||||
DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
EXPORTS cancel, cancelFailed, cancelled;
|
||||
|
||||
IMPORTS
|
||||
OPERATION, ERROR
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)};
|
||||
-- TCInvokeIdSet
|
||||
-- FROM TCAPMessages {itu-t recommendation q 773 modules(2) messages(1)
|
||||
-- version3(3)};
|
||||
|
||||
cancel OPERATION ::= {
|
||||
-- ARGUMENT present < TCInvokeIdSet
|
||||
ARGUMENT INTEGER (-128..127)
|
||||
-- a TC-user may redefine this type to include
|
||||
-- an empty result so that it becomes a Class 1 operation
|
||||
ERRORS {cancelFailed}
|
||||
}
|
||||
|
||||
-- timer = 15 s
|
||||
cancelFailed ERROR ::= {
|
||||
PARAMETER SET {
|
||||
problem [0] CancelProblem,
|
||||
-- invokeId [1] present < TCInvokeIdSet
|
||||
invokeId [1] INTEGER (-128..127)
|
||||
}
|
||||
CODE local:0
|
||||
}
|
||||
|
||||
CancelProblem ::= ENUMERATED {unknownInvocation(0), tooLate(1), notCancellable(2)}
|
||||
|
||||
-- a TC-user may redefine this type to include application-specific problems
|
||||
cancelled ERROR ::= {
|
||||
CODE local:0
|
||||
}
|
||||
|
||||
-- an error of this type should be included in the error list of cancellable operations
|
||||
END
|
|
@ -0,0 +1,89 @@
|
|||
TCAPMessages {itu-t recommendation q 773 modules(2) messages(1) version3(3)}
|
||||
DEFINITIONS IMPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
-- EXPORTS everything
|
||||
-- Transaction Portion fields.
|
||||
IMPORTS
|
||||
ROS{}, InvokeId
|
||||
FROM Remote-Operations-Generic-ROS-PDUs {joint-iso-itu-t
|
||||
remote-operations(4) generic-ROS-PDUs(6) version1(0)}
|
||||
OPERATION
|
||||
FROM Remote-Operations-Information-Objects {joint-iso-itu-t
|
||||
remote-operations(4) informationObjects(5) version1(0)};
|
||||
|
||||
TCMessage{OPERATION:Invokable, OPERATION:Returnable} ::= CHOICE {
|
||||
unidirectional [APPLICATION 1] Unidirectional{{Invokable}, {Returnable}},
|
||||
begin [APPLICATION 2] Begin{{Invokable}, {Returnable}},
|
||||
end [APPLICATION 4] End{{Invokable}, {Returnable}},
|
||||
continue [APPLICATION 5] Continue{{Invokable}, {Returnable}},
|
||||
abort [APPLICATION 7] Abort
|
||||
}
|
||||
|
||||
Unidirectional{OPERATION:Invokable, OPERATION:Returnable} ::= SEQUENCE {
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion{{Invokable}, {Returnable}}
|
||||
}
|
||||
|
||||
Begin{OPERATION:Invokable, OPERATION:Returnable} ::= SEQUENCE {
|
||||
otid OrigTransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion{{Invokable}, {Returnable}} OPTIONAL
|
||||
}
|
||||
|
||||
End{OPERATION:Invokable, OPERATION:Returnable} ::= SEQUENCE {
|
||||
dtid DestTransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion{{Invokable}, {Returnable}} OPTIONAL
|
||||
}
|
||||
|
||||
Continue{OPERATION:Invokable, OPERATION:Returnable} ::= SEQUENCE {
|
||||
otid OrigTransactionID,
|
||||
dtid DestTransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion{{Invokable}, {Returnable}} OPTIONAL
|
||||
}
|
||||
|
||||
Abort ::= SEQUENCE {
|
||||
dtid DestTransactionID,
|
||||
reason CHOICE {p-abortCause P-AbortCause,
|
||||
u-abortCause DialoguePortion} OPTIONAL
|
||||
}
|
||||
-- NOTE - When the Abort Message is generated by the Transaction sublayer, a p-Abort Cause may be
|
||||
-- present. The u-abortCause may be generated by the component sublayer in which case it is an ABRT
|
||||
-- APDU, or by the TC-User in which case it could be either an ABRT APDU or data in some user-defined
|
||||
-- abstract syntax.
|
||||
|
||||
DialoguePortion ::= [APPLICATION 11] EXPLICIT EXTERNAL
|
||||
-- The dialogue portion carries the dialogue control PDUs as value of the external data type. The direct
|
||||
-- reference should be set to {itu-t recommendation q 773 as(1) dialogue-as(1) version1(1)} if structured
|
||||
-- dialogue is used and to {itu-t recommendation q 773 as(1) unidialogue-as(2) version1(1)} if unstructured
|
||||
-- dialogue is used.
|
||||
|
||||
OrigTransactionID ::= [APPLICATION 8] OCTET STRING(SIZE (1..4))
|
||||
|
||||
DestTransactionID ::= [APPLICATION 9] OCTET STRING(SIZE (1..4))
|
||||
|
||||
P-AbortCause ::= [APPLICATION 10] INTEGER {
|
||||
unrecognizedMessageType(0), unrecognizedTransactionID(1),
|
||||
badlyFormattedTransactionPortion(2), incorrectTransactionPortion(3),
|
||||
resourceLimitation(4)}(0..127)
|
||||
|
||||
-- COMPONENT PORTION. The last field in the transaction portion of the TCAP message is the
|
||||
-- component portion. The component portion may be absent.
|
||||
ComponentPortion{OPERATION:Invokable, OPERATION:Returnable} ::=
|
||||
[APPLICATION 12] SEQUENCE SIZE (1..MAX) OF Component{{Invokable}, {Returnable}}
|
||||
|
||||
-- Component Portion fields
|
||||
-- Recommendation X.880 defines four Application Protocol Data Units (APDUs) for invoking
|
||||
-- operations, returning results or error, and for the rejection of invalid PDUs.
|
||||
-- TCAP adds returnResultNotLast to allow for the segmentation of a result.
|
||||
Component{OPERATION:Invokable, OPERATION:Returnable} ::= CHOICE {
|
||||
basicROS ROS{{TCInvokeIdSet}, {Invokable}, {Returnable}},
|
||||
returnResultNotLast [7] returnResult < ROS{{}, {}, {Returnable}}
|
||||
}
|
||||
|
||||
TCInvokeIdSet ::= InvokeId(WITH COMPONENTS { present (-128..127) })
|
||||
|
||||
|
||||
END -- TCAPMessages
|
|
@ -0,0 +1,58 @@
|
|||
TR {joint-iso-itu-t(2) country(16) ca(124) mti(113594) tcap(10) modules(0) itu-t(0) tr(0) version1(1)}
|
||||
DEFINITIONS IMPLICIT TAGS ::=
|
||||
BEGIN
|
||||
|
||||
IMPORTS
|
||||
Abort, DialoguePortion, OrigTransactionID, DestTransactionID
|
||||
FROM TCAPMessages {itu-t recommendation q 773 modules(2) messages(1) version3(3)};
|
||||
|
||||
-- The Transaction sub-layer ignores the content of the ComponentPortion
|
||||
-- so we have redefined these Types without parameterization and defined
|
||||
-- ComponentPortion as an OCTET STRING so that it can be passed to the
|
||||
-- Component sub-layer for decoding there
|
||||
|
||||
TCMessage ::= CHOICE {
|
||||
unidirectional [APPLICATION 1] Unidirectional,
|
||||
begin [APPLICATION 2] Begin,
|
||||
end [APPLICATION 4] End,
|
||||
continue [APPLICATION 5] Continue,
|
||||
abort [APPLICATION 7] Abort,
|
||||
...
|
||||
}
|
||||
|
||||
Unidirectional ::= SEQUENCE {
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion
|
||||
}
|
||||
|
||||
Begin ::= SEQUENCE {
|
||||
otid OrigTransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion OPTIONAL
|
||||
}
|
||||
|
||||
End ::= SEQUENCE {
|
||||
dtid DestTransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion OPTIONAL
|
||||
}
|
||||
|
||||
Continue ::= SEQUENCE {
|
||||
otid OrigTransactionID,
|
||||
dtid DestTransactionID,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion OPTIONAL
|
||||
}
|
||||
|
||||
-- used to derive otid/dtid from unknown message type
|
||||
Unknown ::= SEQUENCE {
|
||||
otid OrigTransactionID OPTIONAL,
|
||||
dtid DestTransactionID OPTIONAL,
|
||||
dialoguePortion DialoguePortion OPTIONAL,
|
||||
components ComponentPortion OPTIONAL
|
||||
}
|
||||
|
||||
-- leave this portion undecoded
|
||||
ComponentPortion ::= TYPE-IDENTIFIER.&Type
|
||||
|
||||
END
|
|
@ -0,0 +1,20 @@
|
|||
UnidialoguePDUs {itu-t recommendation q 773 modules(2) unidialoguePDUs(3)
|
||||
version1(1)} DEFINITIONS ::=
|
||||
BEGIN
|
||||
|
||||
EXPORTS uniDialogue-as-id, UniDialoguePDU;
|
||||
|
||||
-- Abstract syntax name for unstructured dialogue APDUs
|
||||
uniDialogue-as-id OBJECT IDENTIFIER ::=
|
||||
{itu-t recommendation q 773 as(1) unidialogue-as(2) version1(1)}
|
||||
|
||||
UniDialoguePDU ::= CHOICE {unidialoguePDU AUDT-apdu}
|
||||
|
||||
AUDT-apdu ::= [APPLICATION 0] IMPLICIT SEQUENCE {
|
||||
protocol-version
|
||||
[0] IMPLICIT BIT STRING {version1(0)} DEFAULT {version1},
|
||||
application-context-name [1] OBJECT IDENTIFIER,
|
||||
user-information [30] IMPLICIT SEQUENCE OF EXTERNAL OPTIONAL
|
||||
}
|
||||
|
||||
END -- UNIDialoguePDU
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
.PHONY: all
|
||||
all:
|
||||
cd itu && $(MAKE)
|
||||
cd ansi && $(MAKE)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
cd itu && $(MAKE) $@
|
||||
cd ansi && $(MAKE) $@
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
.PHONY: all
|
||||
all: doc
|
||||
|
||||
.PHONY: doc
|
||||
doc:
|
||||
cd html && $(MAKE) $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
cd html && $(MAKE) $@
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
## $Id: Makefile,v 1.3 2005/02/10 06:39:56 vances Exp $
|
||||
|
||||
ERL = erl
|
||||
|
||||
SRC = ../../src
|
||||
|
||||
%.html:${SRC}/%.erl
|
||||
${ERL} -noshell -run edoc_run file '"$<"' '[{dir,"."}]' -s init stop
|
||||
|
||||
%.html:${SRC}/itu/%.erl
|
||||
${ERL} -noshell -run edoc_run file '"$<"' '[{dir,"."}]' -s init stop
|
||||
|
||||
DOCS = tcap.html tcap_tco_server.html
|
||||
|
||||
.PHONY: default
|
||||
default: doc
|
||||
|
||||
.PHONY: doc
|
||||
doc: $(DOCS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
- rm -f $(DOCS)
|
||||
|
After Width: | Height: | Size: 164 KiB |
After Width: | Height: | Size: 133 KiB |
After Width: | Height: | Size: 90 KiB |
|
@ -0,0 +1,359 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>TCAP User's Guide</title>
|
||||
<link rel="stylesheet" type="text/css" href="stylesheet.css">
|
||||
</head>
|
||||
<body bgcolor="white">
|
||||
<h1>TCAP User's Guide</h1>
|
||||
<p>Copyright © 2003-2005 <a href=http://www.motivity.ca>Motivity Telecom Inc.</a></p>
|
||||
<p><b>Version:</b> 1.1</p>
|
||||
<p><b>Authors:</b> Vance Shipley (<a href="mailto:vances@motivity.ca"><tt>vances@motivity.ca</tt></a>).</p>
|
||||
|
||||
<p>The <a href="tcap.html"><tt>tcap</tt></a> application is a protocol stack
|
||||
implementation of the Transaction Capabilities Application Part (TCAP) of
|
||||
the Signaling System No. 7 (SS7) specifications <cite>ITU-T Q.771-Q.774</cite> and
|
||||
<cite>ANSI T1.114-2000</cite>. Transaction Capabilities (TC) users include
|
||||
the Intelligent Network Application Protocol (INAP) ITU-T Q.1208 and the Mobile
|
||||
Application Part (MAP) 3GPP 29.002.</p>
|
||||
|
||||
<h3>Requirements</h3>
|
||||
<p>This application includes only the TCAP procedures and must be used
|
||||
with a seperate application providing the SCCP service.
|
||||
The <a href="http://www.motivity.ca/nms"><tt>nms</tt></a>
|
||||
application provides a binding to the embedded SCCP layer on the
|
||||
TX3220/TX4000 boards. An native Erlang SCCP User Adaptation (SUA)
|
||||
for SIGTRAN SCTP is planned.</p>
|
||||
|
||||
<h3>Transaction Capabilities</h3>
|
||||
<p>Transaction Capabilities (TC) provides support for interactive
|
||||
applications in a distributed environment through a generic remote procedure
|
||||
call service. TC provides the framework for invoking remote procedures
|
||||
and returning the results of these procedures.</p>
|
||||
<a href="transaction_capabilities.png">Figure 1-1</a>
|
||||
shows the structure of TC using SS7 network services. TC is composed of two
|
||||
sublayers; the Component Sublayer (CSL) and the Transaction Sublayer (TSL).
|
||||
The CSL deals with components which are the Application Protocol Data Units (APDU)
|
||||
that convey remote operations and their responses. The CSL optionally may
|
||||
utilize the dialogue portion protocol for conveying information related to
|
||||
application context or user information. The TSL deals with the exchange of
|
||||
messages containing components and optionally, a dialogue portion, between TC-Users.</p>
|
||||
|
||||
<p>Figure 1-1: TC in SS7
|
||||
<img alt="diagram of transaction capabilities" name="figure1-1" src="transaction_capabilities.png"></p>
|
||||
|
||||
<h3>Open Systems Interconnection (OSI)</h3>
|
||||
<p>TC is based on the Remote Operations concept defined in Recommendation X.880 (ROS).
|
||||
TC allows communication between TC-Users across an SS7 network.
|
||||
This communication can be modelled with the OSI seven layer stack as shown in
|
||||
<a href="osi_model.png">Figure-2.1</a>. SS7 does not define an Intermediate Services Part
|
||||
(ISP) so the Presentation, Session and Transport layers are formally missing however
|
||||
some aspects of the these are present in TC. CSL lies entirely within the application layer.</p>
|
||||
<p>Figure 2-1: TC in OSI
|
||||
<img alt="diagram of osi layers" name="figure2-1" src="osi_model.png"></p>
|
||||
|
||||
<a href="application_process.png">Figure-2.2</a> shows the structure of the OSI Application Layer.
|
||||
An Application Process (AP) consists of application code within and outside the OSI
|
||||
framework. The part of an application which resides in the OSI framework is called
|
||||
an Application Entity (AE). The AE may contain a number of cooperating components,
|
||||
each with it's own protocol elements. These components are called Application
|
||||
Service Elements (ASE). An ASE is a separately defined (standardized) part of an
|
||||
Application Entity. ASEs provide a service to higher level ASEs not a higher level
|
||||
layer. The distinction being that unlike layer services an ASE service may consider
|
||||
only part of the communication between Application Entities.</p>
|
||||
<p>Figure 2-2: OSI Application Process
|
||||
<img alt="diagram of application process" name="figure2-2" src="application_process.png"></p>
|
||||
|
||||
<p>The Component sublayer is in partial alignment with the capabilities of the
|
||||
Remote Operation Service Element (ROSE) X.219 and X.229. The X.229 protocol is
|
||||
contained within the TC component protocol. CSL includes some extensions to ROSE.
|
||||
The dialogue control facilities are in partial alignment with the capabilities
|
||||
of the Association Control Service Element (ACSE) X.217 and X.227. The abstract
|
||||
syntax for the dialogue control APDUs are a subset of the OSI ACSE abstract syntax.</p>
|
||||
<a href="tcap_application_process.png">Figure-2.3</a> shows an Application Process
|
||||
with an Application Entity which includes the Transaction Capabilities ASE and the
|
||||
Mobile Application Part ASE.</p>
|
||||
<p>Figure 2-3: SS7 Application Process
|
||||
<img alt="diagram of SS7 application process" name="figure2-3" src="tcap_application_process.png"></p>
|
||||
|
||||
<p>An Application Entity (AE) is the part of your Application Process (AP) which
|
||||
uses the services of a combined set of ASEs. An AE-Type defines a set of functions
|
||||
used for communications. For example one AE-Type may combine a TC ASE with a MAP ASE
|
||||
while another combines a TC ASE with an INAP ASE. An AE Invocation (AEI) is an instance
|
||||
of an AE and it's ASEs.</p>
|
||||
|
||||
<p>An AEI may perform a subset of the communication functions defined by the AE-Type.
|
||||
The actual procedures that may need to be performed for an instance of communication
|
||||
are determined by the Application Context (AC). The Application Context states which
|
||||
functions are needed. Based on this information the AEI is instantiated from the
|
||||
AE-Type which fits these criteria.</p>
|
||||
|
||||
<p>Using the <a href="tcap.html"><tt>tcap</tt></a> application you will implement
|
||||
your Application Process (AP) and Application Entity (AE) in Erlang. The set of
|
||||
processes which make up an instance of the Component Sublayer (CSL) form the TC ASE.
|
||||
For each new dialogue an AEI including a TC ASE and a TC-User ASE (e.g. MAP) is created.
|
||||
The AE uses the ASEs together to provide higher level functions to the AP.</p>
|
||||
<p>Figure 3-1: AE Invocations
|
||||
<img alt="diagram of AE invocations" name="figure3-1" src="ae_invocations.png"></p>
|
||||
|
||||
<p>For example you may have an AE which uses TCAP and MAP ASEs implemented in a
|
||||
<tt>gen_fsm</tt> callback module named ae_map_v3. An AEI for a location update
|
||||
could be created as:
|
||||
<pre>
|
||||
gen_fsm:start_link(ae_map_v3, ['networkLocUpContext-v3', TSL], [])</pre>
|
||||
<p>Where <tt>'networkLocUpContext-v3'</tt> is the application context name and
|
||||
<tt>TSL</tt> is a reference to the transaction sublayer used for this operation.
|
||||
The callback module would start TC and MAP and coordinate them to provide the
|
||||
location update service to the Application Program.</p>
|
||||
<pre>
|
||||
-module(ae_map_v3).
|
||||
-export([init/1, handle_event/2]).
|
||||
-behaviour(gen_fsm).
|
||||
-record(state, {ac, tsl, csl, map}).
|
||||
|
||||
init([AC, TSL]) ->
|
||||
case tcap:open(TSL, self(), []) of
|
||||
{ok, CSL} ->
|
||||
case map:open(CSL, self(), []) of
|
||||
{ok, MAP} ->
|
||||
State = #state{ac = AC, tsl = TSL, csl = CSL, map = MAP},
|
||||
{ok, idle, State};
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
</pre>
|
||||
|
||||
<p>In ASN.1 an <tt>OPERATION-PACKAGE</tt> type is used to define an ASE. An
|
||||
<tt>APPLICATION-CONTEXT</tt> type defines an AC. An application protocol is defined
|
||||
by the set of all possible ACs. The above example uses these definitions from the
|
||||
3GPP/GSM MAP specification:</p>
|
||||
|
||||
<pre>
|
||||
networkLocUpContext-v3 APPLICATION-CONTEXT ::= {
|
||||
-- Responder is HLR if Initiator is VLR
|
||||
INITIATOR CONSUMER OF {locationUpdatingPackage-v3 | dataRestorationPackage-v3}
|
||||
RESPONDER CONSUMER OF {subscriberDataMngtPackage-v3 | tracingPackage-v3}
|
||||
ID {map-ac networkLocUp(1) version3(3)}
|
||||
}
|
||||
</pre>
|
||||
<pre>
|
||||
locationUpdatingPackage-v3 OPERATION-PACKAGE ::= {
|
||||
-- Supplier is HLR if Consumer is VLR
|
||||
CONSUMER INVOKES {updateLocation}
|
||||
SUPPLIER INVOKES {forwardCheckSs-Indication}
|
||||
}
|
||||
</pre>
|
||||
<pre>
|
||||
updateLocation OPERATION ::= { --Timer m
|
||||
ARGUMENT UpdateLocationArg
|
||||
RESULT UpdateLocationRes
|
||||
ERRORS {systemFailure | dataMissing | unexpectedDataValue | unknownSubscriber | roamingNotAllowed}
|
||||
CODE local:2
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>In a complex AE there may be multiple TC-User ASEs. The operation codes
|
||||
(e.g. <tt>local:2</tt> for the <tt>updateLocation</tt> above) of the received
|
||||
components allow the AE to distribute to the appropriate ASE. While the
|
||||
<tt>locationUpdatingPackage-v3</tt> definition above appears informally in the
|
||||
current specifications MAP is still viewed as having a single Application Service Element
|
||||
for historical reasons. The Intelligent Network Application Protocol (INAP) however
|
||||
clearly defines many distinct ASEs. <a href="inap_aei.png">Figure 3-2</a>
|
||||
shows the configuration of an AEI for INAP using the
|
||||
<tt>scf-to-ssf-status-reporting-v1</tt> AC.</p>
|
||||
<p>Figure 3-2: Example INAP AEI
|
||||
<img alt="diagram of INAP AEI" name="figure3-2" src="inap_aei.png"></p>
|
||||
|
||||
<h3>Addressing</h3>
|
||||
<p>When used with SS7 network services the addressesing of the Signaling Connection
|
||||
Control Part (SCCP) is used. The SCCP CalledParty and CallingParty address formats
|
||||
are used in the TCAP address parameters; Destination Address and Originating Address.
|
||||
These parameters identify the destination and originating TC-user.</p>
|
||||
<p>The SCCP Subsystem Number (SSN) is used by SCCP for message distrubution to
|
||||
seperate instances of the TCAP Transaction Sublayer (TSL).</p>
|
||||
<p>Figure 4-1: SSN Distribution
|
||||
<img alt="diagram of ssn distribution" name="figure4-1" src="ssn_distribution.png"></p>
|
||||
|
||||
|
||||
<h3>Process Communication</h3>
|
||||
<p>A number of processes interact to provide the TCAP service.
|
||||
<a href="tcap_messaging.png">Figure 5-1</a> depicts the message paths
|
||||
between processes used with the TCAP application.</p>
|
||||
|
||||
<p>The TCAP protocol layer is split into two sublayers; the Transaction
|
||||
Sublayer and the Component Sublayer.</p>
|
||||
|
||||
<p>In the transaction sublayer a transaction coordinator (TCO)
|
||||
process performs marshalling of incoming indications from the
|
||||
SCCP service access point (SAP). It spawns a transaction state
|
||||
machine (TSM) for each new transaction.</p>
|
||||
|
||||
<p>In the component sublayer a dialogue handler (DHA) process
|
||||
is started for each transaction. It then spawns a component
|
||||
coordinator process (CCO). For a remotely initiated transaction
|
||||
DHA is started by TCO. For a locally initiated transaction DHA
|
||||
is started by the TC-User. An invocation state machine (ISM)
|
||||
is started for each locally invoked operation involved in the
|
||||
transaction.<p>
|
||||
|
||||
<p>Figure 5-1<br>
|
||||
<img alt="diagram of process communication" name="figure5-1" src="tcap_messaging.png"></p>
|
||||
|
||||
<h3>Modules</h3>
|
||||
|
||||
<h5><tt>tcap</tt></h5>
|
||||
<p>This module implements the application programming interface (API) for the application.</p>
|
||||
|
||||
<h5><tt>tcap_app</tt></h5>
|
||||
<p>This is the start module for the application.
|
||||
It is an <tt>application</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_sup</tt></h5>
|
||||
<p>This module implements the top level supervisor for the application.
|
||||
It is a <tt>supervisor</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_sap_sup</tt></h5>
|
||||
<p>This module implements a supervisor at the SAP level, one per SAP.
|
||||
It is a <tt>supervisor</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_tco_server</tt></h5>
|
||||
<p>This module implements the transaction coordinator (TCO).
|
||||
It is a <tt>gen_server</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_transaction_sup</tt></h5>
|
||||
<p>This module implements a supervisor at the transaction level, one per transaction.
|
||||
It is a <tt>supervisor</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_tsm_fsm</tt></h5>
|
||||
<p>This module implements the transaction state machine (TSM).
|
||||
It is a <tt>gen_fsm</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_dialogue_sup</tt></h5>
|
||||
<p>This module implements a supervisor at the dialogue level, one per dialogue (transaction).
|
||||
It is a <tt>supervisor</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_dha_fsm</tt></h5>
|
||||
<p>This module implements the dialogue handler (DHA).
|
||||
It is a <tt>gen_fsm</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_components_sup</tt></h5>
|
||||
<p>This module implements a supervisor at the components level, one per component sequence (dialogue/transaction).
|
||||
It is a <tt>supervisor</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_cco_server</tt></h5>
|
||||
<p>This module implements the component coordinator (CCO).
|
||||
It is a <tt>gen_server</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_invocation_sup</tt></h5>
|
||||
<p>This module implements a supervisor at the invocation level, one per component sequence (dialogue/transaction).
|
||||
It is a <tt>supervisor</tt> behaviour callback module.</p>
|
||||
|
||||
<h5><tt>tcap_ism_fsm</tt></h5>
|
||||
<p>This module implements the invocation state machine (ISM).
|
||||
It is a <tt>gen_fsm</tt> behaviour callback module.</p>
|
||||
|
||||
<h3>Supervision Tree</h3>
|
||||
|
||||
<p>The processes which make up an instance of the TCAP service layer are all instantiated within a single
|
||||
supervision tree. When the application is started a top level supervisor is created with no children.
|
||||
The user calls <a href="tcap.html#open-3"><tt>tcap:open/3</tt></a> to create a new
|
||||
service access point (SAP) which dynamically adds a <tt>tcap_sap_sup</tt> supervisor with one worker TCO.</p>
|
||||
|
||||
<a href="tcap_supervision.png">Figure 5-2</a> shows the structure of the supervision tree.</p>
|
||||
|
||||
<p>For every new transaction ID assigned (Begin indication or request) a <tt>tcap_transaction_sup</tt>
|
||||
supervisor is dynamically added to the <tt>tcap_sap_sup</tt> supervisor with one TSM worker and a
|
||||
<tt>tcap_dialogue_sup</tt> supervisor. In the case of a Unidirectional primitive no transaction is
|
||||
assigned. Instead a <tt>tcap_dialogue_sup</tt> supervisor is dynamically added to the <tt>tcap_sap_sup</tt>
|
||||
supervisor.</p>
|
||||
<p>When a <tt>tcap_dialogue_sup</tt> supervisor is started it immediately creates a DHA worker and a
|
||||
<tt>tcap_components_sup</tt> supervisor with one CCO worker and a <tt>tcap_invocation_sup</tt> supervisor.</p>
|
||||
<p>A <tt>tcap_ism_fsm</tt> worker is dynamically added to the <tt>tcap_invocation_sup</tt> supervisor for
|
||||
each locally invoked operation.</p>
|
||||
|
||||
<p>Figure 5-2<br>
|
||||
<img alt="diagram of supervision tree" name="figure5-2" src="tcap_supervision.png"></p>
|
||||
|
||||
|
||||
<h3>Distribution</h3>
|
||||
|
||||
<p>In order to facilitate building very large systems the processes involved may be distributed across nodes.
|
||||
<a href="tcap_distribution.png">Figure 6-1</a> shows some examples of how this distribution may be accomplished.</p>
|
||||
|
||||
<p>Figure 6-1<br>
|
||||
<img alt="diagram of process distribution" name="figure6-1" src="tcap_distribution.png"></p>
|
||||
|
||||
<p>When a SAP is created with <a href="tcap.html#open-3"><tt>tcap:open/3</tt></a> the
|
||||
<a href="tcap_tco_server.html"><tt>tcap_tco_server</tt></a> callback module name is provided to start a TCO
|
||||
which can adapt the specific implementation of SCCP in use to this TCAP service layer. Arguments are also
|
||||
passed to specify instance specific configuration such as SCCP subsystem number.</p>
|
||||
<p>The <a href="tcap_tco_server.html"><tt>tcap_tco_server</tt></a> callback module exports start functions
|
||||
used to create SAP, TCO, TSM and DHA processes. This allows the user to configure how the application is
|
||||
distributed.</p>
|
||||
<p>The DHA always starts the CCO and ISM processes on it's local node.</p>
|
||||
|
||||
|
||||
<h3>Primitives (ITU)</h3>
|
||||
|
||||
<h4>TC-User → Component Sublayer</h4>
|
||||
<h5>Dialogue Handling</h5>
|
||||
<tt>{'TC', 'UNI', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'BEGIN', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'CONTINUE', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'END', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-ABORT', request, Parms}</tt><br>
|
||||
<h5>Component Handling</h5>
|
||||
<tt>{'TC', 'INVOKE', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'RESULT-L', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'RESULT-NL', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-ERROR', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-CANCEL', request, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-REJECT', request, Parms}</tt><br>
|
||||
|
||||
<h4>Component Sublayer → TC-User</h4>
|
||||
<h5>Dialogue Handling</h5>
|
||||
<tt>{'TC', 'UNI', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'BEGIN', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'END', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-ABORT', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'P-ABORT', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'NOTICE', indication, Parms}</tt><br>
|
||||
<h5>Component Handling</h5>
|
||||
<tt>{'TC', 'INVOKE', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'RESULT-L', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'RESULT-NL', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-ERROR', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'L-CANCEL', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'L-REJECT', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'R-REJECT', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'U-REJECT', indication, Parms}</tt><br>
|
||||
<tt>{'TC', 'TIMER-RESET', indication, Parms}</tt><br>
|
||||
|
||||
<h4>Component Sublayer → Transaction Sublayer</h4>
|
||||
<tt>{'TR', 'UNI', request, Parms}</tt><br>
|
||||
<tt>{'TR', 'BEGIN', request, Parms}</tt><br>
|
||||
<tt>{'TR', 'CONTINUE', request, Parms}</tt><br>
|
||||
<tt>{'TR', 'END', request, Parms}</tt><br>
|
||||
<tt>{'TR', 'U-ABORT', request, Parms}</tt><br>
|
||||
|
||||
<h4>Transaction Sublayer → Component Sublayer</h4>
|
||||
<tt>{'TR', 'UNI', indication, Parms}</tt><br>
|
||||
<tt>{'TR', 'BEGIN', indication, Parms}</tt><br>
|
||||
<tt>{'TR', 'CONTINUE', indication, Parms}</tt><br>
|
||||
<tt>{'TR', 'END', indication, Parms}</tt><br>
|
||||
<tt>{'TR', 'U-ABORT', indication, Parms}</tt><br>
|
||||
<tt>{'TR', 'P-ABORT', indication, Parms}</tt><br>
|
||||
<tt>{'TR', 'NOTICE', indication, Parms}</tt><br>
|
||||
|
||||
<h4>Transaction Sublayer → SCCP</h4>
|
||||
<tt>{'N', 'UNITDATA', request, Parms}</tt><br>
|
||||
|
||||
<h4>SCCP → Transaction Sublayer</h4>
|
||||
<tt>{'N', 'UNITDATA', indication, Parms}</tt><br>
|
||||
<tt>{'N', 'NOTICE', indication, Parms}</tt><br>
|
||||
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 253 KiB |
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1,13 @@
|
|||
{application, tcap,
|
||||
[{description, "Transaction Capabilities Application Part"},
|
||||
{vsn, "0.1"},
|
||||
{modules, [tcap, tcap_app, tcap_sup,
|
||||
transaction_coordinator_sup, transaction_coordinator_server,
|
||||
transaction_sup, transaction_fsm,
|
||||
dialogue_sup, dialogue_fsm,
|
||||
component_cordinator_sup, component_cordinator,
|
||||
invocation_sup, invocation_fsm]},
|
||||
{registered, []},
|
||||
{applications, [kernel, stdlib, sccp]},
|
||||
{env, [{supref, {local, tcap_sup}}]},
|
||||
{mod, {tcap_app, []}}]}.
|
|
@ -0,0 +1,102 @@
|
|||
%%% $Id: sccp_adaptation.erl,v 1.1 2005/02/11 03:32:45 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @author Vance Shipley <vances@motivity.ca>
|
||||
%%%
|
||||
%%% @doc Example Transaction Coordinator (TCO) adaptation callback module.
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_example_adaptation).
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.1 $').
|
||||
|
||||
-behaviour(tcap_tco_server).
|
||||
|
||||
% export the gen_server call backs
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
% export the tcap_tco_server specific call backs
|
||||
-export([send_primitive/2, start_transaction/2, start_dialogue/2]).
|
||||
|
||||
-record(state, {nsap}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The tcap_tco_server specific call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% @spec (Primitive, State) -> void()
|
||||
%% Primitive = {'N', 'UNITDATA', request, UdataParams}
|
||||
%%
|
||||
%% @doc Deliver service primitive to the SCCP layer.
|
||||
%%
|
||||
send_primitive(Primitive, State) ->
|
||||
State#state.nsap ! Primitive.
|
||||
|
||||
%% @spec (DialogueID, CSL, State) -> pid()
|
||||
%% DialogueID = int()
|
||||
%%
|
||||
%% @doc Start a MAP dialogue state machine (DSM) TC-User process.
|
||||
%%
|
||||
start_user(DialogueID, CSL, State) ->
|
||||
map:open(State#state.map, DialogueID, CSL).
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% @spec (Args) -> Result.
|
||||
%% Args = [NSAP:int()]
|
||||
%% Result = {ok, State}
|
||||
%%
|
||||
%% @doc Initialize the Transaction Coordinator (TCO).
|
||||
%%
|
||||
init([NSAP]) ->
|
||||
case sccp:start_link(self(), NSAP) of
|
||||
{ok, NSAP} ->
|
||||
case map:start_link(self(), NSAP) of
|
||||
{ok, MAP} ->
|
||||
{ok, #state{nsap = NSAP, map = MAP}}.
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% @hidden
|
||||
%%
|
||||
handle_call(_Event, _From, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%% @spec (Primitive, State) -> Result.
|
||||
%% Primitive = {'N', GenericName, indication, Parameter}
|
||||
%% GenericName = 'UNITDATA' | 'NOTICE'
|
||||
%% Parameter = #'UNITDATA'{} | #'NOTICE'{}
|
||||
%% State = #state{}
|
||||
%%
|
||||
%% @doc Handle a service primitive received from the SCCP SAP.
|
||||
%%
|
||||
handle_info({'N', _, indication, _} = Primitive, State) ->
|
||||
{primitive, Primitive, State}.
|
||||
|
||||
%% @hidden
|
||||
%%
|
||||
handle_cast(_Event, State) ->
|
||||
{noreply, State};
|
||||
|
||||
%% @hidden
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
%% @hidden
|
||||
code_change(_OldVersion, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% internal functions
|
||||
%%----------------------------------------------------------------------
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
%%% $Id: tcap.hrl,v 1.1 2005/01/04 05:43:50 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% Copyright Motivity Telecom Inc. 2004
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
|
||||
%%%
|
||||
%%% ITU-T recommendation Q.771 Functional Decsription of
|
||||
%%% Transaction Capabilities describes the primitives and
|
||||
%%% their parameters used in the TC-Service and TR-Service
|
||||
%%% interfaces. Each primitive has a record defined here
|
||||
%%% containing it's parameters. Modules using the these
|
||||
%%% services utilize these records to format messages:
|
||||
%%%
|
||||
%%% {'TC', 'BEGIN', request, Begin} when is_record(Begin, 'TC-BEGIN')
|
||||
%%% {'TR', 'UNI', indication, Unidirectional} when is_record(Unidirectional, 'TR-UNI')
|
||||
%%%
|
||||
|
||||
%%%
|
||||
%%% TC-User
|
||||
%%%
|
||||
%%% componentsPresent is a boolean
|
||||
|
||||
%% reference: Table 3/Q.771 - TC-UNI primitives
|
||||
-record('TC-UNI', {qos, destAddress, appContextName, origAddress, dialogueID,
|
||||
userInfo, componentsPresent}).
|
||||
|
||||
%% reference: Table 4/Q.771 - TC-BEGIN primitives
|
||||
-record('TC-BEGIN', {qos, destAddress, appContextName, origAddress, dialogueID,
|
||||
userInfo, componentsPresent}).
|
||||
|
||||
%% reference: Table 5/Q.771 - TC-CONTINUE primitives
|
||||
-record('TC-CONTINUE', {qos, origAddress, appContextName, dialogueID,
|
||||
userInfo, componentsPresent}).
|
||||
|
||||
%% reference: Table 7/Q.771 - TC-END primitives
|
||||
-record('TC-END', {qos, dialogueID, appContextName, componentsPresent,
|
||||
userInfo, termination}).
|
||||
|
||||
%% reference: Table 8/Q.771 - TC-U-ABORT primitives
|
||||
-record('TC-U-ABORT', {qos, dialogueID, abortReason, appContextName, userInfo}).
|
||||
%% abortReason is one of applicationContextNotSupported, dialogueRefused, userSpecific
|
||||
|
||||
%% reference: Table 9/Q.771 - TC-NOTICE primitives
|
||||
-record('TC-NOTICE', {dialogueID, origAddress, destAddress, reportCause}).
|
||||
|
||||
%% reference: Table 10/Q.771 - Operation invocation primitives
|
||||
-record('TC-INVOKE', {dialogueID, class, invokeID, linkedID, operation,
|
||||
parameters, lastComponent, timeout}).
|
||||
|
||||
%% reference: Table 11/Q.771 - Report of success primitives
|
||||
-record('TC-RESULT-L', {dialogueID, invokeID, operation, parameters,
|
||||
lastComponent}).
|
||||
-record('TC-RESULT-NL', {dialogueID, invokeID, operation, parameters,
|
||||
lastComponent}).
|
||||
|
||||
%% reference: Table 12/Q.771 - Report of failure primitives
|
||||
-record('TC-U-ERROR', {dialogueID, invokeID, error, parameters,
|
||||
lastComponent}).
|
||||
|
||||
%% reference: Table 13/Q.771 - User rejection primitives
|
||||
-record('TC-U-REJECT', {dialogueID, invokeID, problemCode,
|
||||
lastComponent}).
|
||||
|
||||
%% reference: Table 14/Q.771 - TC-CANCEL primitives
|
||||
-record('TC-L-CANCEL', {dialogueID, invokeID}).
|
||||
-record('TC-U-CANCEL', {dialogueID, invokeID}).
|
||||
|
||||
%% reference: Table 14bis/Q.771 TC-TIMER-RESET primitives
|
||||
-record('TC-TIMER-RESET', {dialogueID, invokeID}).
|
||||
|
||||
%% reference: Table 15/Q.771 - Component sublayer rejection primitives
|
||||
-record('TC-L-REJECT', {dialogueID, invokeID, problemCode, lastComponent}).
|
||||
-record('TC-R-REJECT', {dialogueID, invokeID, problemCode, lastComponent}).
|
||||
|
||||
%% reference: Table 16/Q.771 - Primitive for Abort
|
||||
-record('TC-P-ABORT', {qos, dialogueID, pAbort}).
|
||||
%% pAbort is either a P-AbortCause or abnormalDialogue or noCommonDialoguePortion
|
||||
|
||||
|
||||
%%%
|
||||
%%% TR-User
|
||||
%%%
|
||||
%%% userData is a 'TR-user-data' record
|
||||
%%%
|
||||
%%% Q.771 does not provide a distinction between the dialogue portion and
|
||||
%%% component portion within the user data parameter. In 3.1.1 it says
|
||||
%%% "a dialogue portion is formatted and sent concatenated with the
|
||||
%%% (sequence of) components(s)."
|
||||
%%% This is probably due to dialogue handling being added after Q.771 was
|
||||
%%% first written. We will define a record for the user data.
|
||||
-record('TR-user-data', {dialoguePortion, componentPortion}).
|
||||
|
||||
%% reference: Table 18/Q.771 - TR-UNI primitives
|
||||
-record('TR-UNI', {qos, destAddress, origAddress, userData}).
|
||||
|
||||
%% reference: Table 19/Q.771 - Primitives for transaction begin
|
||||
-record('TR-BEGIN', {qos, destAddress, origAddress, transactionID, userData}).
|
||||
|
||||
%% reference: Table 20/Q.771 - Transaction continuation primitives
|
||||
-record('TR-CONTINUE', {qos, origAddress, transactionID, userData}).
|
||||
|
||||
%% reference: Table 22/Q.771 - TR-END primitives
|
||||
-record('TR-END', {qos, transactionID, termination, userData}).
|
||||
|
||||
%% reference: Table 23/Q.771 - TR-U-ABORT primitives
|
||||
-record('TR-U-ABORT', {qos, transactionID, userData}).
|
||||
|
||||
%% reference: Table 24/Q.771 - Transaction sublayer abort primitive
|
||||
-record('TR-P-ABORT', {qos, transactionID, pAbort}).
|
||||
%% pAbort is a P-AbortCause
|
||||
|
||||
%% reference: Table 25/Q.771 - TR-NOTICE primitive
|
||||
-record('TR-NOTICE', {transactionID, origAddress, destAddress, reportCause}).
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
## $id$
|
||||
|
||||
SCCPINCDIR = ../../../SCCP/itu/include
|
||||
|
||||
EBIN = ../../ebin/itu
|
||||
|
||||
ERLC = erlc
|
||||
EMULATOR = beam
|
||||
ERLCFLAGS = -b $(EMULATOR) -o $(EBIN) -W -v +warn_unused_vars -I ../asn_src -I ../include -I $(SCCPINCDIR)
|
||||
|
||||
$(EBIN)/%.$(EMULATOR):%.erl
|
||||
$(ERLC) $(ERLCFLAGS) $<
|
||||
|
||||
BEAMS = $(EBIN)/tcap_tco_server.$(EMULATOR) \
|
||||
$(EBIN)/tcap_tsm_fsm.$(EMULATOR) \
|
||||
$(EBIN)/tcap_dha_fsm.$(EMULATOR) \
|
||||
$(EBIN)/tcap_cco_server.$(EMULATOR) \
|
||||
$(EBIN)/tcap_ism_fsm.$(EMULATOR)
|
||||
|
||||
.PHONY: default
|
||||
default: all
|
||||
|
||||
.PHONY: all
|
||||
all: $(BEAMS)
|
||||
|
||||
.PHONY: install
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
- rm -f $(BEAMS)
|
||||
|
||||
tcap_tco_server.$(EMULATOR): tcap_tco_server.erl \
|
||||
../../asn_src/TCAPMessages.hrl ../../include/tcap.hrl \
|
||||
$(SCCPINCDIR)/sccp.hrl
|
||||
tcap_tsm_fsm.$(EMULATOR): tcap_tsm_fsm.erl \
|
||||
../../asn_src/TCAPMessages.hrl ../../include/tcap.hrl \
|
||||
$(SCCPINCDIR)/sccp.hrl
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
%%% $Id: queryWithPerm_fsm.erl,v 1.5 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc ANSI TCAP'Query with Permission' package.
|
||||
%%% <p>This module implements an ANSI TCAP 'Query with Permission' package
|
||||
%%% type transaction process. The components within the received
|
||||
%%% transaction PDU are executed sequentially in this process.</p>
|
||||
%%%
|
||||
%%% @reference ANSI T1.114-2000
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
|
||||
|
||||
-module(queryWithPerm_fsm).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.5 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
|
||||
terminate/3, code_change/4]).
|
||||
|
||||
%% call backs for gen_fsm states in this module
|
||||
%% our published API functions
|
||||
-export([]).
|
||||
|
||||
-include("TCAPPackage.hrl").
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init([SccpParms, TransactionPDU]) ->
|
||||
{CalledParty, CallingParty, QualityOfServiceParameterSet} = SccpParms,
|
||||
{ok,
|
||||
process_flag(trap_exit, true),
|
||||
{ok, StateName, StateData, Timeout}.
|
||||
|
||||
%% handle events sent with gen_fsm:send_all_state_event/2
|
||||
handle_event(Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
% trapped exit signals
|
||||
handle_info({'EXIT', Pid, Reason}, State) ->
|
||||
{stop, Reason, State}.
|
||||
|
||||
%% handle events sent with gen_fsm:sync_send_all_state_event/2,3
|
||||
handle_sync_event(Event, From, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
% handle other received messages
|
||||
handle_info(Unknown, State) ->
|
||||
error_logger:error_msg("Received unknown message: ~p~n", [Unknown]),
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
% someone wants us to shutdown and cleanup
|
||||
terminate(Reason, State) -> ok.
|
||||
|
||||
% upgrading the running code
|
||||
code_change(_OldVsn, StateName, StateData, _Extra) ->
|
||||
{ok, StateName, StateData}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% The states for the 'Query with Permission' finite state machine
|
||||
%%%
|
||||
|
||||
package_sent(timeout, StateData) ->
|
||||
{next_state, StateName, StateData, Timeout};
|
||||
package_sent(Event, StateData) ->
|
||||
{next_state, StateName, StateData, Timeout}.
|
||||
|
||||
package_received(timeout, StateData) ->
|
||||
{next_state, StateName, StateData, Timeout};
|
||||
package_received(Event, StateData) ->
|
||||
{next_state, StateName, StateData, Timeout}.
|
||||
|
||||
%%%
|
||||
%%% internal functions
|
||||
%%%
|
|
@ -0,0 +1,80 @@
|
|||
%%% $Id: tcap_cco_server.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc TCAP Component Coordinator (CCO) functional block within the
|
||||
%%% component sub-layer of ANSI TCAP.
|
||||
%%%
|
||||
%%% @reference ANSI T1.114.4 Transaction Capabilities Procedures
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_cco_server).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% call backs needed for gen_server behaviour
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {supervisor, usap, dialogueID}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init([Supervisor, USAP, DialogueID]) ->
|
||||
process_flag(trap_exit, true),
|
||||
{ok, #state{supervisor = Supervisor, usap = USAP, dialogueID = DialogueID}}.
|
||||
|
||||
%% shutdown the server
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, shutdown, ok, State};
|
||||
|
||||
%% unrecognized calls
|
||||
handle_call(Other, From, State) ->
|
||||
error_logger:error_report([{unknown_call, Other}, {from, From}]),
|
||||
{noreply, State}.
|
||||
|
||||
%% unrecognized casts
|
||||
handle_cast(Other, State) ->
|
||||
error_logger:error_report([{unknown_cast, Other}]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
%% trapped exit signals
|
||||
handle_info({'EXIT', _Pid, Reason}, State) ->
|
||||
{stop, Reason, State};
|
||||
|
||||
%% unknown messages
|
||||
handle_info(Unknown, State) ->
|
||||
error_logger:error_msg("Received unknown message: ~p~n", [Unknown]),
|
||||
{noreply, State}.
|
||||
|
||||
%% someone wants us to shutdown and cleanup
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%% upgrading the running code
|
||||
code_change(_, _, _) -> ok.
|
||||
|
||||
%%%
|
||||
%%% internal functions
|
||||
%%%
|
||||
|
|
@ -0,0 +1,809 @@
|
|||
%%% $Id: tcap_dha_fsm.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Dialogue Handler (DHA) functional block within the component
|
||||
%%% sub-layer of ANSI TCAP.
|
||||
%%%
|
||||
%%% @reference ANSI T1.114.4 Transaction Capabilities Procedures
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
|
||||
-module(tcap_dha_fsm).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_info/3, handle_event/3, handle_sync_event/4,
|
||||
terminate/3, code_change/4]).
|
||||
|
||||
%% transaction_fsm state callbacks
|
||||
-export([idle/2, wait_for_uni_components/2, wait_for_begin_components/2,
|
||||
initiation_received/2, wait_cont_components_ir/2,
|
||||
wait_cont_components_active/2, wait_for_end_components/2,
|
||||
initiation_sent/2, active/2]).
|
||||
|
||||
%% record definitions for TR-User primitives
|
||||
-include("tcap.hrl").
|
||||
%% record definitions for N-User primitives
|
||||
-include("sccp.hrl").
|
||||
%% record definitions for TCAP messages
|
||||
%-include("TCAPMessages.hrl").
|
||||
%-include("UnidialoguePDUs.hrl").
|
||||
%-include("DialoguePDUs.hrl").
|
||||
|
||||
%% the dialogue_fsm state data
|
||||
-record(state, {usap, tco, supid, cco, otid, did, parms, appContextMode}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_fsm call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% Start the Dialogue Handler (DHA) process
|
||||
%% reference: Figure A.5/Q.774 (sheet 1 of 11)
|
||||
init({USAP, DialogueID, TCO, Supervisor}) ->
|
||||
init({USAP, DialogueID, TCO, undefined, Supervisor});
|
||||
init({USAP, DialogueID, TCO, SupId, Supervisor}) ->
|
||||
%% Start a Component Coordinator (CCO) process
|
||||
ChildName = list_to_atom("cco_sup_" ++ integer_to_list(DialogueID)),
|
||||
StartFunc = {supervisor, start_link, [component_coordinator_sup, [USAP, DialogueID]]},
|
||||
ChildSpec = {ChildName, StartFunc, permanent, infinity,
|
||||
supervisor, [component_coordinator_sup]},
|
||||
{ok, CCO} = supervisor:start_child(Supervisor, ChildSpec),
|
||||
process_flag(trap_exit, true),
|
||||
{ok, idle, #state{usap = USAP, did = DialogueID,
|
||||
tco = TCO, supid = SupId, cco = CCO}}.
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 1 of 11)
|
||||
%%% TC-UNI request from TCU
|
||||
idle({'TC', 'UNI', request, UniParms}, State)
|
||||
when is_record(UniParms, 'TC-UNI') ->
|
||||
%% Dialogue info included?
|
||||
case UniParms#'TC-UNI'.userInfo of
|
||||
undefined ->
|
||||
DialoguePortion = undefined;
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
%% Build AUDT apdu
|
||||
DialoguePortion = 'UnidialoguePDUs':encode('AUDT-apdu',
|
||||
#'AUDT-apdu'{'application-context-name' = UniParms#'TC-UNI'.appContextName,
|
||||
'user-information' = UserInfo})
|
||||
end,
|
||||
TrParms = #'TR-UNI'{qos = UniParms#'TC-UNI'.qos,
|
||||
destAddress = UniParms#'TC-UNI'.destAddress,
|
||||
origAddress = UniParms#'TC-UNI'.origAddress,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms},
|
||||
%% Request components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_uni_components, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 1 of 11)
|
||||
%%% TC-BEGIN request from TCU
|
||||
idle({'TC', 'BEGIN', request, BeginParms}, State)
|
||||
when is_record(BeginParms, 'TC-BEGIN') ->
|
||||
%% Dialogue info included?
|
||||
case BeginParms#'TC-BEGIN'.userInfo of
|
||||
undefined ->
|
||||
DialoguePortion = undefined;
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
%% Set protocol version = 1
|
||||
%% Build AARQ apdu
|
||||
DialoguePortion = 'DialoguePDUs':encode('AARQ-apdu',
|
||||
#'AARQ-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = BeginParms#'TC-BEGIN'.appContextName,
|
||||
'user-information' = UserInfo})
|
||||
end,
|
||||
TrParms = #'TR-BEGIN'{qos = BeginParms#'TC-BEGIN'.qos,
|
||||
destAddress = BeginParms#'TC-BEGIN'.destAddress,
|
||||
origAddress = BeginParms#'TC-BEGIN'.origAddress,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms,
|
||||
%% Set application context mode
|
||||
appContextMode = BeginParms#'TC-BEGIN'.appContextName},
|
||||
%% Request components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_begin_components, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 2 of 11)
|
||||
%%% TR-UNI indication from TSL
|
||||
idle({'TR', 'UNI', indication, UniParms}, State) when is_record(UniParms, 'TR-UNI') ->
|
||||
%% Extract dialogue portion
|
||||
case extract_uni_dialogue_portion(UniParms#'TR-UNI'.userData) of
|
||||
incorrect_dialogue_portion -> %% Dialogue portion correct? (no)
|
||||
%% Discard components
|
||||
{stop, normal, State};
|
||||
no_version1 -> %% Is version 1 supported? (no)
|
||||
%% Discard components
|
||||
{stop, normal, State};
|
||||
TcParms when is_record(TcParms, 'TC-UNI') ->
|
||||
if
|
||||
is_record(UniParms#'TR-UNI'.userData, 'TR-user-data'),
|
||||
(UniParms#'TR-UNI'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (UniParms#'TR-UNI'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Assign dialogue ID
|
||||
DialogueID = transaction_coordinator_server:new_tid(),
|
||||
NewTcParms = TcParms#'TC-UNI'{qos = UniParms#'TR-UNI'.qos,
|
||||
destAddress = UniParms#'TR-UNI'.destAddress,
|
||||
origAddress = UniParms#'TR-UNI'.origAddress,
|
||||
dialogueID = DialogueID,
|
||||
componentsPresent = ComponentsPresent},
|
||||
NewState = State#state{did = DialogueID, parms = NewTcParms},
|
||||
%% Components to CHA
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok % should never happen
|
||||
end,
|
||||
%% TC-UNI indication to TCU
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'UNI', indication, NewTcParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 3 of 11)
|
||||
%%% TR-BEGIN indication from TSL
|
||||
idle({'TR', 'BEGIN', indication, BeginParms}, State) when is_record(BeginParms, 'TR-BEGIN') ->
|
||||
%% Extract dialogue portion
|
||||
case extract_begin_dialogue_portion(BeginParms#'TR-BEGIN'.userData) of
|
||||
incorrect_dialogue_portion -> %% Dialogue portion correct? (no)
|
||||
%% Build ABORT apdu
|
||||
ABRT = 'DialoguePDUs':encode('ABRT-apdu', #'ABRT-apdu'{'abort-source' = 'dialogue-service-provider'}),
|
||||
%% Discard components
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = {transactionID = BeginParms#'TR-BEGIN'.transactionID,
|
||||
userData = #'TR-user-data'{dialoguePortion = ABRT}},
|
||||
NewState = State#state{otid = BeginParms#'TR-BEGIN'.transactionID, parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
no_version1 -> %% Is version 1 supported? (no)
|
||||
DialoguePortion = (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.dialoguePortion,
|
||||
%% Build AARE apdu
|
||||
AARE = 'DialoguePDUs':encode('AARE-apdu', #'AARE-apdu'{
|
||||
'protocol-version' = version1,
|
||||
'application-context-name' = DialoguePortion#'AARQ-apdu'.'application-context-name',
|
||||
result = reject-permanent,
|
||||
'result-source-diagnostic' = {'dialogue-service-provider', 'no-common-dialogue-portion'}}),
|
||||
%% Discard components
|
||||
%% TR-P-ABORT request to TSL
|
||||
TrParms = {transactionID = BeginParms#'TR-P-ABORT'.transactionID, pAbort = AARE},
|
||||
NewState = State#state{otid = BeginParms#'TR-BEGIN'.transactionID,
|
||||
appContextMode = DialoguePortion#'AARQ-apdu'.'application-context-name',
|
||||
parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'P-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
TcParms when is_record(TcParms, 'TC-BEGIN') ->
|
||||
if
|
||||
is_record(BeginParms#'TR-BEGIN'.userData, 'TR-user-data'),
|
||||
(BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Assign dialogue ID
|
||||
DialogueID = transaction_coordinator_server:new_tid(),
|
||||
NewTcParms = TcParms#'TC-BEGIN'{qos = BeginParms#'TR-BEGIN'.qos,
|
||||
destAddress = BeginParms#'TR-BEGIN'.destAddress,
|
||||
origAddress = BeginParms#'TR-BEGIN'.origAddress,
|
||||
dialogueID = DialogueID,
|
||||
componentsPresent = ComponentsPresent},
|
||||
NewState = State#state{otid = BeginParms#'TR-BEGIN'.transactionID, did = DialogueID,
|
||||
parms = NewTcParms, appContextMode = TcParms#'TC-BEGIN'.appContextName},
|
||||
%% TC-BEGIN indication to TCU
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'BEGIN', indication, NewTcParms}),
|
||||
%% Any components?
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
%% Components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
{next_state, initiation_received, NewState}
|
||||
end.
|
||||
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
%%% TC-CONTINUE request from TCU
|
||||
initiation_received({'TC', 'CONTINUE', request, ContParms}, State) when is_record(ContParms, 'TC-CONTINUE') ->
|
||||
%% Dialogue info included?
|
||||
case ContParms#'TC-CONTINUE'.userInfo of
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
AARE = #'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = ContParms#'TC-CONTINUE'.appContextName,
|
||||
result = accepted,
|
||||
'result-source-diagnostic' = {'dialogue-service-user', null},
|
||||
'user-information' = UserInfo},
|
||||
DialoguePortion = 'DialoguePDUs':encode('AARE-apdu', AARE),
|
||||
TrParms = #'TR-CONTINUE'{qos = ContParms#'TC-CONTINUE'.qos,
|
||||
origAddress = ContParms#'TR-CONTINUE'.origAddress,
|
||||
transactionID = State#state.otid,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms};
|
||||
undefined ->
|
||||
NewState = State
|
||||
end,
|
||||
{next_state, wait_cont_components_ir, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
%%% TC-END request from TCU
|
||||
initiation_received({'TC', 'END', request, EndParms}, State) when is_record(EndParms, 'TC-END') ->
|
||||
%% Prearranged end?
|
||||
case EndParms#'TC-END'.termination of
|
||||
prearranged ->
|
||||
%% TR-END request to TSL
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'END', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
basic ->
|
||||
%% Dialogue info included?
|
||||
case EndParms#'TC-END'.userInfo of
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
AARE = #'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = EndParms#'TC-END'.appContextName,
|
||||
result = accepted,
|
||||
'result-source-diagnostic' = {'dialogue-service-user', null},
|
||||
'user-information' = UserInfo},
|
||||
DialoguePortion = 'DialoguePDUs':encode('AARE-apdu', AARE),
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms};
|
||||
undefined ->
|
||||
NewState = State
|
||||
end,
|
||||
%% Request components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_end_components, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 6 of 11)
|
||||
%%% TC-U-ABORT request from TCU
|
||||
initiation_received({'TC', 'U-ABORT', request, AbortParms}, State) when is_record(AbortParms, 'TC-U-ABORT'),
|
||||
(AbortParms#'TC-U-ABORT'.abortReason == applicationContextNotSupported)
|
||||
or (AbortParms#'TC-U-ABORT'.abortReason == dialogueRefused)
|
||||
or (AbortParms#'TC-U-ABORT'.abortReason == userSpecified) ->
|
||||
case State#state.appContextMode of
|
||||
%% Is application context mode set? (no)
|
||||
undefined ->
|
||||
UserData = #'TR-user-data'{};
|
||||
%% Abort reason present and = AC-name not supported OR dialogue refused?
|
||||
_AppContextName when AbortParms#'TC-U-ABORT'.abortReason == applicationContextNotSupported ->
|
||||
%% Set protocol version = 1
|
||||
%% Build AARE-pdu (rejected)
|
||||
AARE = 'DialoguePDUs':encode('AARE-apdu',
|
||||
#'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = AbortParms#'TC-U-ABORT'.appContextName,
|
||||
result = 'reject-permanent',
|
||||
'result-source-diagnostic' = {'dialogue-service-user', 'application-context-name-not-supported'}}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = AARE};
|
||||
_AppContextName when AbortParms#'TC-U-ABORT'.abortReason == dialogueRefused ->
|
||||
%% Set protocol version = 1
|
||||
%% Build AARE-pdu (rejected)
|
||||
AARE = 'DialoguePDUs':encode('AARE-apdu',
|
||||
#'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = AbortParms#'TC-U-ABORT'.appContextName,
|
||||
result = 'reject-permanent',
|
||||
'result-source-diagnostic' = {'dialogue-service-user', null}}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = AARE};
|
||||
_AppContextName when AbortParms#'TC-U-ABORT'.abortReason == userSpecified ->
|
||||
%% Build ABRT-apdu (abort source = dialogue-service-user)
|
||||
ABRT = 'DialoguePDUs':encode('ABRT-apdu',
|
||||
#'ABRT-apdu'{'abort-source' = 'dialogue-service-user',
|
||||
'user-information' = AbortParms#'TC-U-ABORT'.userInfo}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = ABRT}
|
||||
end,
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = #'TR-U-ABORT'{qos = AbortParms#'TC-U-ABORT'.qos,
|
||||
transactionID = State#state.otid,
|
||||
userData = UserData},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}.
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%%% TC-END request from TCU
|
||||
initiation_sent({'TC', 'END', request, EndParms}, State) when is_record(EndParms, 'TC-END'),
|
||||
EndParms#'TC-END'.termination == prearranged -> % termination must be prearranged
|
||||
%% TR-END request to TSL
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'END', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%%% TC-U-ABORT request from TCU (local action)
|
||||
initiation_sent({'TC', 'U-ABORT', request, AbortParms}, State) when is_record(AbortParms, 'TC-U-ABORT') ->
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = #'TR-U-ABORT'{qos = AbortParms#'TC-U-ABORT'.qos, transactionID = State#state.otid},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%%% TR-END indication from TSL
|
||||
initiation_sent({'TR', 'END', indication, EndParms}, State) when is_record(EndParms, 'TR-END') ->
|
||||
if
|
||||
is_record(EndParms#'TR-END'.userData, 'TR-user-data'),
|
||||
(EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Dialogue portion included?
|
||||
%% AC Mode set?
|
||||
%% Extract dialogue portion
|
||||
%% Dialogue portion correct?
|
||||
case extract_dialogue_portion(EndParms#'TR-END'.userData, State#state.appContextMode) of
|
||||
abort ->
|
||||
%% Discard components
|
||||
%% TC-P-ABORT indication to TCU
|
||||
TcParms = #'TC-P-ABORT'{qos = EndParms#'TR-END'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
AARE ->
|
||||
%% TC-END indication to TCU
|
||||
TcParms = #'TC-END'{qos = EndParms#'TR-END'.qos,
|
||||
dialogueID = State#state.did,
|
||||
appContextName = State#state.appContextMode,
|
||||
componentsPresent = ComponentsPresent,
|
||||
userInfo = AARE,
|
||||
termination = EndParms#'TR-END'.termination},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'END', indication, TcParms}),
|
||||
%% Any components?
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
%% Components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%% NOTE: currently the TCO short circuits this function and sends directly to TCU
|
||||
initiation_sent({'TR', 'NOTICE', indication, NoticeParms}, State) when is_record(NoticeParms, 'TR-NOTICE') ->
|
||||
%% TC-NOTICE indication to TCU
|
||||
TcParms = #'TC-NOTICE'{dialogueID = State#state.did,
|
||||
origAddress = NoticeParms#'TR-NOTICE'.origAddress,
|
||||
destAddress = NoticeParms#'TR-NOTICE'.destAddress,
|
||||
reportCause = NoticeParms#'TR-NOTICE'.reportCause},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'NOTICE', indication, TcParms}),
|
||||
{next_state, initiation_sent, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 8 of 11)
|
||||
%% TR-CONTINUE indication from TSL
|
||||
initiation_sent({'TR', 'CONTINUE', indication, ContParms}, State) when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
if
|
||||
is_record(ContParms#'TR-CONTINUE'.userData, 'TR-user-data'),
|
||||
(ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (ContParms#'TR-END'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Dialogue portion included?
|
||||
%% AC Mode set?
|
||||
%% Extract dialogue portion
|
||||
%% Dialogue portion correct?
|
||||
case extract_dialogue_portion(ContParms#'TR-CONTINUE'.userData, State#state.appContextMode) of
|
||||
abort ->
|
||||
%% Discard components
|
||||
%% TC-P-ABORT indication to TCU
|
||||
TcParms = #'TC-P-ABORT'{qos = ContParms#'TR-CONTINUE'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms}),
|
||||
%% Build ABRT apdu
|
||||
ABRT = 'DialoguePDUs':encode('ABRT-apdu',
|
||||
#'ABRT-apdu'{'abort-source' = 'dialogue-service-provider'}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = ABRT},
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = #'TR-U-ABORT'{qos = ContParms#'TC-U-ABORT'.qos,
|
||||
transactionID = NewState#state.otid, userData = UserData},
|
||||
LastState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(LastState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(LastState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, LastState};
|
||||
AARE ->
|
||||
%% TC-CONTINUE indication to TCU
|
||||
TcParms = #'TC-CONTINUE'{qos = ContParms#'TR-CONTINUE'.qos,
|
||||
origAddress = ContParms#'TR-CONTINUE'.origAddress,
|
||||
appContextName = State#state.appContextMode,
|
||||
dialogueID = State#state.did,
|
||||
userInfo = AARE,
|
||||
componentsPresent = ComponentsPresent},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'END', indication, TcParms}),
|
||||
%% Any components?
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
%% Components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
{next_state, active, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 8 of 11)
|
||||
%% TR-U-ABORT indication from TSL
|
||||
initiation_sent({'TR', 'U-ABORT', indication, AbortParms}, State) when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
case catch begin
|
||||
if
|
||||
%% Is AC mode set? (no) Is Dialogue portion present? (no)
|
||||
State#state.appContextMode == undefined and (not is_record(AbortParms#'TR-U-ABORT'.userData, 'TR-user-data')
|
||||
or (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion == undefined) ->
|
||||
throw(#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos, dialogueID = State#state.did});
|
||||
%% Is AC mode set? (no) Is Dialogue portion present? (yes)
|
||||
State#state.appContextMode == undefined, is_record(AbortParms#'TR-U-ABORT'.userData, 'TR-user-data'),
|
||||
AbortParms#'TR-U-ABORT'.userData /= undefined ->
|
||||
throw(#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did, pAbort = abnormalDialogue});
|
||||
%% Is User Data included in primitive? (no)
|
||||
not is_record(AbortParms#'TR-U-ABORT'.userData, 'TR-user-data');
|
||||
(AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion == undefined ->
|
||||
throw(#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did, pAbort = abnormalDialogue});
|
||||
true -> ok
|
||||
end,
|
||||
%% Is PDU type = ABRT or AARE (rejected)?
|
||||
case 'DialoguePDUs':decode('DialoguePDU', (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion) of
|
||||
%% Is abstract syntax = dialogue-PDU AS? (no)
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'),
|
||||
APDU#'AARE-apdu'.'application-context-name' /= State#state.appContextMode ->
|
||||
#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue};
|
||||
%% Is Abort source = user? (yes)
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'ABRT-apdu'),
|
||||
element(1, APDU#'ABRT-apdu'.'abort-source') == 'dialogue-service-user' ->
|
||||
#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
abortReason = userSpecific,
|
||||
userInfo = APDU#'ABRT-apdu'.'user-information'};
|
||||
%% Is Associate source = user? (yes)
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'), APDU#'AARE-apdu'.'result-source-diagnostic'
|
||||
== {'dialogue-service-user', 'application-context-name-not-supported'},
|
||||
APDU#'AARE-apdu'.result == 'reject-permanent' ->
|
||||
#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
abortReason = applicationContextNotSupported,
|
||||
appContextName = APDU#'AARE-apdu'.'application-context-name',
|
||||
userInfo = APDU#'AARE-apdu'.'user-information'};
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'),
|
||||
element(1, APDU#'AARE-apdu'.'result-source-diagnostic') == 'dialogue-service-user',
|
||||
APDU#'AARE-apdu'.result == 'reject-permanent' ->
|
||||
#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
abortReason = dialogueRefused,
|
||||
appContextName = APDU#'AARE-apdu'.'application-context-name',
|
||||
userInfo = APDU#'AARE-apdu'.'user-information'};
|
||||
%% Is AARE (no common dialogue portion)?
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'),
|
||||
APDU#'AARE-apdu'.'result-source-diagnostic' == {'dialogue-service-provider', 'no-common-dialogue-portion'} ->
|
||||
#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = noCommonDialoguePortion};
|
||||
_ ->
|
||||
#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue}
|
||||
end
|
||||
end of
|
||||
TcParms when is_record(TcParms, 'TC-U-ABORT') ->
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'U-ABORT', indication, TcParms});
|
||||
TcParms when is_record(TcParms, 'TC-P-ABORT') ->
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms})
|
||||
end,
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 8 of 11)
|
||||
%% TR-P-ABORT indication from TSL
|
||||
initiation_sent({'TR', 'P-ABORT', indication, AbortParms}, State) when is_record(AbortParms, 'TR-P-ABORT') ->
|
||||
TcParms = #'TC-P-ABORT'{qos = AbortParms#'TR-P-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = AbortParms#'TR-P-ABORT'.pAbort},
|
||||
NewState = State#state{parms = TcParms},
|
||||
%% TC-P-ABORT indication to TCU
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}.
|
||||
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
%% TC-CONTINUE request from TCU
|
||||
active({'TC', 'CONTINUE', request, ContParms}, State) when is_record(ContParms, 'TC-CONTINUE') ->
|
||||
NewState = State#state{parms = ContParms},
|
||||
%% Request component to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_cont_components_active, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
%% TC-END request from TCU
|
||||
active({'TC', 'END', request, EndParms}, State) when is_record(EndParms, 'TC-END') ->
|
||||
%% Prearranged end?
|
||||
case EndParms#'TC-END'.termination of
|
||||
prearranged ->
|
||||
%% TR-END request to TSL
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'END', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
basic ->
|
||||
%% Request component to CHA
|
||||
gen_fsm:send_event(State#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_end_components, State}
|
||||
end.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 2 of 11)
|
||||
wait_for_uni_components('no-component', State) ->
|
||||
wait_for_uni_components1(State);
|
||||
wait_for_uni_components({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-UNI'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-UNI'{userData = NewUserData},
|
||||
wait_for_uni_components1(State#state{parms = TrParms}).
|
||||
wait_for_uni_components1(State) ->
|
||||
%% TR-UNI request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'UNI', request, State#state.parms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(State#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, State}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 2 of 11)
|
||||
wait_for_begin_components('no-component', State) ->
|
||||
wait_for_begin_components1(State);
|
||||
wait_for_begin_components({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-BEGIN'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-BEGIN'{userData = NewUserData},
|
||||
wait_for_begin_components1(State#state{parms = TrParms}).
|
||||
wait_for_begin_components1(State) ->
|
||||
%% Assign local transaction ID
|
||||
TrParms = (State#state.parms)#'TR-BEGIN'{transactionID = transaction_coordinator_server:new_tid()},
|
||||
%% TR-BEGIN request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'BEGIN', request, TrParms}),
|
||||
{next_state, initiation_sent, State#state{parms = TrParms}}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
wait_cont_components_ir('no-component', State) ->
|
||||
wait_cont_components_ir1(State);
|
||||
wait_cont_components_ir({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-CONTINUE'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-CONTINUE'{userData = NewUserData},
|
||||
wait_cont_components_ir1(State#state{parms = TrParms}).
|
||||
wait_cont_components_ir1(State) ->
|
||||
%% TR-CONTINUE request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'CONTINUE', request, State#state.parms}),
|
||||
{next_state, initiation_sent, State}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
wait_cont_components_active('no-component', State) ->
|
||||
wait_cont_components_active1(State);
|
||||
wait_cont_components_active({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-CONTINUE'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-CONTINUE'{userData = NewUserData},
|
||||
wait_cont_components_active1(State#state{parms = TrParms}).
|
||||
wait_cont_components_active1(State) ->
|
||||
%% TR-CONTINUE request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'CONTINUE', request, State#state.parms}),
|
||||
{next_state, active, State}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
wait_for_end_components('no-component', State) ->
|
||||
wait_for_end_components1(State);
|
||||
wait_for_end_components({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-END'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-END'{userData = NewUserData},
|
||||
wait_for_end_components1(State#state{parms = TrParms}).
|
||||
wait_for_end_components1(State) ->
|
||||
%% TR-END request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'END', request, State#state.parms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(State#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, State}.
|
||||
|
||||
%% Dialogue portion included? (yes)
|
||||
extract_uni_dialogue_portion(UserData) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Dialogue portion correct?
|
||||
case 'UnidialoguePDUs':decode('UnidialoguePDU', UserData#'TR-user-data'.dialoguePortion) of
|
||||
{unidialoguePDU, AUDT} when is_record(AUDT, 'AUDT-apdu') ->
|
||||
%% Is version 1 supported?
|
||||
case lists:member(version1, AUDT#'AUDT-apdu'.'protocol-version') of
|
||||
true ->
|
||||
#'TC-UNI'{appContextName = AUDT#'AUDT-apdu'.'application-context-name',
|
||||
userInfo = AUDT#'AUDT-apdu'.'user-information'};
|
||||
false ->
|
||||
no_version1
|
||||
end;
|
||||
_ ->
|
||||
incorrect_dialogue_portion
|
||||
end;
|
||||
%% Dialogue portion included? (no)
|
||||
extract_uni_dialogue_portion(_DialoguePortion) ->
|
||||
#'TC-UNI'{}.
|
||||
|
||||
|
||||
%% Dialogue portion included? (yes)
|
||||
extract_begin_dialogue_portion(UserData) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Dialogue portion correct?
|
||||
case 'DialoguePDUs':decode('DialoguePDU', UserData#'TR-user-data'.dialoguePortion) of
|
||||
{dialoguePDU, AARQ} when is_record(AARQ, 'AARQ-apdu') ->
|
||||
%% Is version 1 supported?
|
||||
case lists:member(version1, AARQ#'AARQ-apdu'.'protocol-version') of
|
||||
true ->
|
||||
%% Set application context mode
|
||||
#'TC-BEGIN'{appContextName = AARQ#'AARQ-apdu'.'application-context-name',
|
||||
userInfo = AARQ#'AARQ-apdu'.'user-information'};
|
||||
false ->
|
||||
no_version1
|
||||
end;
|
||||
_ ->
|
||||
incorrect_dialogue_portion
|
||||
end;
|
||||
%% Dialogue portion included? (no)
|
||||
extract_begin_dialogue_portion(_DialoguePortion) ->
|
||||
#'TC-BEGIN'{}.
|
||||
|
||||
extract_dialogue_portion(UserData, undefined) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Dialogue portion included? (yes) AC mode set? (no)
|
||||
abort;
|
||||
extract_dialogue_portion(UserData, _AppContextName) when not is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion == undefined ->
|
||||
%% Dialogue portion included? (no) AC mode set? (yes)
|
||||
abort;
|
||||
extract_dialogue_portion(UserData, _AppContextName) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Extract dialogue portion
|
||||
case 'DialoguePDUs':decode('DialoguePDU', UserData#'TR-user-data'.dialoguePortion) of
|
||||
{dialoguePDU, AARE} when is_record(AARE, 'AARE-apdu') ->
|
||||
AARE; %% Dialogue portion correct? (yes)
|
||||
_ ->
|
||||
abort %% Dialogue portion correct? (no)
|
||||
end.
|
||||
|
||||
|
||||
%% handle any other message
|
||||
handle_info(Info, StateName, State) ->
|
||||
error_logger:format("dialogue_fsm (~w) received unexpected message: ~w~n", [Info]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle an event sent using gen:fsm_send_all_state_event/2
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%% handle an event sent using gen_fsm:sync_send_all_state_event/2,3
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%% handle a shutdown request
|
||||
terminate(_Reason, _StateName, State) when State#state.supid == undefined ->
|
||||
%% we were started by TSM, no worries
|
||||
ok;
|
||||
terminate(_Reason, _StateName, State) ->
|
||||
%% signal TCO so he can reap the ChildSpec of our supervisor
|
||||
gen_server:cast(State#state.tco, {'dha-stopped', State#state.supid}).
|
||||
|
||||
%% handle updating state data due to a code replacement
|
||||
code_change(_OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
%%% $Id: tcap_ism_fsm.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Invocation State Machine (ISM) functional block within the
|
||||
%%% component sub-layer of ANSI TCAP.
|
||||
%%%
|
||||
%%% @reference ANSI T1.114.4 Transaction Capabilities Procedures
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
|
||||
-module(tcap_ism_fsm).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_event/3, handle_info/3, terminate/3, code_change/4]).
|
||||
|
||||
%% invocation_fsm state callbacks
|
||||
-export([]).
|
||||
|
||||
%% record definitions for TC-User primitives
|
||||
-include("tcap.hrl").
|
||||
%% record definitions for TCAP messages
|
||||
%-include("TCAPMessages.hrl").
|
||||
|
||||
%% the invocation_fsm state data
|
||||
-record(state, {usap, dialogueID, cco}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_fsm call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% Start the Invocation State Machine (ISM) process
|
||||
%% reference: Figure A.7/Q.774 (sheet 1 of 6)
|
||||
|
||||
|
||||
%% handle any other message
|
||||
handle_info(Info, StateName, State) ->
|
||||
error_logger:format("~w (~w) received unexpected message: ~w~n", [?Module, self(), Event]),
|
||||
{next_state, StateName, State};
|
||||
|
||||
%% handle a shutdown request
|
||||
terminate(_Reason, _StateName, State) -> ok.
|
||||
|
||||
%% handle updating state data due to a code replacement
|
||||
code_change(OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
%%% $Id: tcap_server.erl,v 1.5 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc ANSI TCAP service.
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
|
||||
|
||||
-module(tcap_server).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.5 $').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% call backs needed for gen_server behaviour
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
%% our published API functions
|
||||
-export([start_link/1, stop/1]).
|
||||
|
||||
-include("TCAPPackage.hrl").
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The tcap_server exported API
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
start_link(SubSystemNumber) when is_integer(SubSystemNumber) ->
|
||||
RegName = "tcap" ++ "ssn" ++ integer_to_list(SubSystemNumber),
|
||||
gen_server:start_link({local, list_to_atom(RegName)}, ?MODULE,
|
||||
SubSystemNumber, []).
|
||||
|
||||
stop(SubSystemNumber) when is_integer(SubSystemNumber) ->
|
||||
RegName = "tcap" ++ "ssn" ++ integer_to_list(SubSystemNumber),
|
||||
gen_server:call(list_to_atom(RegName), stop).
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init([SubSystemNumber]) when is_integer(SubSystemNumber) ->
|
||||
NSAPName = "sccp_" ++ "ssn" ++ integer_to_list(SubSystemNumber),
|
||||
process_flag(trap_exit, true),
|
||||
{ok, list_to_atom(NSAPName)}.
|
||||
|
||||
%% shutdown the sccp server
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, shutdown, State};
|
||||
|
||||
%% unrecognized calls
|
||||
handle_call(Other, From, State) ->
|
||||
error_logger:error_report([{unknown_call, Other}, {from, From}]),
|
||||
{noreply, State}.
|
||||
|
||||
%%%
|
||||
%%% service primitives received from the TC-User
|
||||
%%%
|
||||
handle_cast({'TC', 'UNIDIRECTIONAL', request, Parameters}, State) ->
|
||||
{noreply, State};
|
||||
handle_cast({'TC', 'QUERY-W', request, Parameters}, State) ->
|
||||
OTID = new_transaction_id(),
|
||||
FsmName = list_to_atom("tr_" ++ integer_to_list(OTID)),
|
||||
FsmArgs = [SccpParms, TransactionPDU],
|
||||
FsmOptions = [],
|
||||
StartArgs = [{local, FsmName}, queryWithPerm_fsm, FsmArgs, FsmOptions],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
supervisor:start_child(tcap_sup, {FsmName, StartFunc, transient, worker,
|
||||
[queryWithPerm_fsm]}),
|
||||
{noreply, State};
|
||||
handle_cast({'TC', 'QUERY-WO', request, Parameters}, State) ->
|
||||
{noreply, State};
|
||||
handle_cast({'TC', 'CONVERSATION-W', request, Parameters}, State) ->
|
||||
{noreply, State};
|
||||
handle_cast({'TC', 'CONVERSATION-WO', request, Parameters}, State) ->
|
||||
{noreply, State};
|
||||
handle_cast({'TC', 'RESPONSE', request, Parameters}, State) ->
|
||||
{noreply, State};
|
||||
handle_cast({'TC', 'ABORT', request, Parameters}, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%%
|
||||
%%% service primitives received from the SCCP service
|
||||
%%%
|
||||
handle_cast({'N', 'UNITDATA', indication, {CalledParty, CallingParty,
|
||||
QualityOfServiceParameterSet, NPDU}}, NSAP) ->
|
||||
SccpParms = {CalledParty, CallingParty, QualityOfServiceParameterSet},
|
||||
%%
|
||||
%% Transaction Portion
|
||||
%% reference T1.114.2 3.1
|
||||
%%
|
||||
{ok, Package} = 'TCAPPackage':decode('PackageType', NPDU),
|
||||
case Package of
|
||||
%%
|
||||
%% Unidirectional
|
||||
%%
|
||||
%% Sends information in one direction only with no reply
|
||||
%% expected. No TCAP Transaction is established.
|
||||
%%
|
||||
{unidirectional, UniTransactionPDU} ->
|
||||
error_logger:error_report([{unhandled_primitive, unidirectional},
|
||||
{caller, Caller}, {called, Called}, {package, Package}]),
|
||||
{noreply, NSAP};
|
||||
%%
|
||||
%% Query with permission
|
||||
%%
|
||||
%% Initiates a TCAP transaction and informs the destination
|
||||
%% it may end the TCAP transaction.
|
||||
%%
|
||||
{queryWithPerm, TransactionPDU} ->
|
||||
OTID = new_transaction_id(),
|
||||
FsmName = list_to_atom("tcap_" ++ integer_to_list(OTID)),
|
||||
FsmArgs = [SccpParms, TransactionPDU],
|
||||
FsmOptions = [],
|
||||
StartArgs = [{local, FsmName}, queryWithPerm_fsm, FsmArgs, FsmOptions],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
supervisor:start_child(tcap_sup, {FsmName, StartFunc, transient, worker,
|
||||
[queryWithPerm_fsm]}),
|
||||
{noreply, NSAP};
|
||||
%%
|
||||
%% Query without permission
|
||||
%%
|
||||
%% Initiates a TCAP transaction and informs the destination
|
||||
%% it may not end the TCAP transaction.
|
||||
%%
|
||||
{queryWithoutPerm, TransactionPDU} ->
|
||||
error_logger:error_report([{unhandled_primitive, queryWithoutPerm},
|
||||
{caller, Caller}, {called, Called}, {package, Package}]),
|
||||
{noreply, NSAP};
|
||||
%%
|
||||
%% Response
|
||||
%%
|
||||
%% Ends a TCAP transaction
|
||||
%%
|
||||
{response, TransactionPDU} ->
|
||||
{noreply, NSAP};
|
||||
%%
|
||||
%% Conversation with permission
|
||||
%%
|
||||
%% Continues a TCAP transaction and informs the destination
|
||||
%% that it may end the TCAP transaction.
|
||||
%%
|
||||
{conversationWithPerm, TransactionPDU} ->
|
||||
error_logger:error_report([{unhandled_primitive, conversationWithPerm},
|
||||
{caller, Caller}, {called, Called}, {package, Package}]),
|
||||
{noreply, NSAP};
|
||||
%%
|
||||
%% Conversation without permission
|
||||
%%
|
||||
%% Continues a TCAP transaction and informs the destination
|
||||
%% that it may not end the TCAP transaction.
|
||||
%%
|
||||
{conversationWithoutPerm, TransactionPDU} ->
|
||||
error_logger:error_report([{unhandled_primitive, conversationWithoutPerm},
|
||||
{caller, Caller}, {called, Called}, {package, Package}]),
|
||||
{noreply, NSAP};
|
||||
%%
|
||||
%% Abort
|
||||
%%
|
||||
%% Informs the destination that the sender has terminated
|
||||
%% the transaction without sending any pending components.
|
||||
%%
|
||||
{abort, Abort} ->
|
||||
error_logger:error_report([{unhandled_primitive, abaort},
|
||||
{caller, Caller}, {called, Called}, {package, Package}]),
|
||||
{noreply, NSAP}
|
||||
end.
|
||||
|
||||
%% unrecognized casts
|
||||
handle_cast(Other, State) ->
|
||||
error_logger:error_report([{unknown_cast, Other}]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
% trapped exit signals
|
||||
handle_info({'EXIT', Pid, Reason}, State) ->
|
||||
{stop, Reason, State};
|
||||
|
||||
% unknown messages
|
||||
handle_info(Unknown, State) ->
|
||||
error_logger:error_msg("Received unknown message: ~p~n", [Unknown]),
|
||||
{noreply, State}.
|
||||
|
||||
% someone wants us to shutdown and cleanup
|
||||
terminate(Reason, State) -> ok.
|
||||
|
||||
% upgrading the running code
|
||||
code_change(_, _, _) -> ok.
|
||||
|
||||
%%%
|
||||
%%% internal functions
|
||||
%%%
|
||||
|
||||
%% get the next originating transaction id from the global counter
|
||||
new_transaction_id() ->
|
||||
ets:update_counter(tcap, transactionID, {2, 1, 16#ffffffff, 0}).
|
|
@ -0,0 +1,386 @@
|
|||
%%% $Id: tcap_tco_server.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Transaction Coordinator (TCO) functional block within the
|
||||
%%% transaction sub-layer of ANSI TCAP.
|
||||
%%%
|
||||
%%% @reference ANSI T1.114.4 Transaction Capabilities Procedures
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
|
||||
-module(tcap_tco_server).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% call backs needed for gen_server behaviour
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
%% our published API functions
|
||||
-export([new_tid/0]).
|
||||
|
||||
-include("TCAPMessages.hrl").
|
||||
-include("tcap.hrl").
|
||||
-include("sccp.hrl").
|
||||
|
||||
-record(state, {supervisor, nsap, usap}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init([Supervisor, NSAP, USAP]) ->
|
||||
% NSAP = "sccp_" ++ "ssn" ++ integer_to_list(SubSystemNumber),
|
||||
process_flag(trap_exit, true),
|
||||
{ok, #state{supervisor = Supervisor, nsap = NSAP, usap = USAP}}.
|
||||
|
||||
%% shutdown the server
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, shutdown, ok, State};
|
||||
|
||||
%% unrecognized calls
|
||||
handle_call(Other, From, State) ->
|
||||
error_logger:error_report([{unknown_call, Other}, {from, From}]),
|
||||
{noreply, State}.
|
||||
|
||||
%%%
|
||||
%%% service primitive indications from the network layer
|
||||
%%%
|
||||
%%% reference: Figure A.3/Q.774 (sheet 1 of 4)
|
||||
%%%
|
||||
handle_cast({'N', 'UNITDATA', indication, UdataParms}, State)
|
||||
when is_record(UdataParms, 'N-UNITDATA') ->
|
||||
case 'TR':decode('TCMessage', UdataParms#'N-UNITDATA'.userData) of
|
||||
{ok, {unidirectional, TPDU}} ->
|
||||
case 'TR':decode('Unidirectional', TPDU) of
|
||||
{ok, Unidirectional} ->
|
||||
%% Create a Dialogue Handler (DHA)
|
||||
DialogueID = new_tid(),
|
||||
SupId = list_to_atom("dha_" ++ integer_to_list(DialogueID)),
|
||||
{ok, {M, F, A, Mods} = application:get_env(start_dha),
|
||||
StartFunc = {M, F, A ++ [{State#state.nsap, State#state.usap, DialogueID, self(), SupId}]},
|
||||
ChildSpec = {SupId, StartFunc, temporary, 10000, worker, Mods},
|
||||
{ok, DHA} = supervisor:start_child(State#state.supervisor, ChildSpec),
|
||||
%% TR-UNI indication CSL <- TSL
|
||||
UserData = #'TR-user-data'{dialoguePortion = Unidirectional#'Unidirectional'.dialoguePortion,
|
||||
componentPortion = Unidirectional#'Unidirectional'.components},
|
||||
TrParms = #'TR-UNI'{qos =
|
||||
destAddress = UdataParms#'N-UNITDATA'.calledAddress,
|
||||
origAddress = UdataParms#'N-UNITDATA'.callingAddress,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(DHA, {'TR', 'UNI', indication, TrParms}),
|
||||
{noreply, State};
|
||||
{error, Reason} ->
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (3)
|
||||
error_logger:error_report(["Syntax error in received N-UNI",
|
||||
{nsap, State#state.nsap}, {error, Reason},
|
||||
{caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {'begin', TPDU}} ->
|
||||
case 'TR':decode('Begin', TPDU) of
|
||||
{ok, Begin} ->
|
||||
%% Assign local transaction ID
|
||||
TID = new_tid(),
|
||||
ChildName = list_to_atom("tsm_sup_" ++ integer_to_list(TID)),
|
||||
{ok, {M, F, A, Mods} = application:get_env(start_tsm),
|
||||
StartFunc = {M, F, A ++ [{State#state.nsap, State#state.usap, TID}]},
|
||||
ChildSpec = {ChildName, StartFunc, temporary, infinity, supervisor, Mods},
|
||||
%% Is TID = no TID?
|
||||
%% Note: The assignment of the ID above just gets the next available
|
||||
%% value and doesn't ensure that it is not in use (unlikely)
|
||||
%% or that there are enough resources available. The real
|
||||
%% test is in whether the start succeeds.
|
||||
case supervisor:start_child(State#state.supervisor, ChildSpec) of
|
||||
{ok, TSM} ->
|
||||
%% Created a Transaction State Machine (TSM)
|
||||
TsmParms = UdataParms#'N-UNITDATA'{userData = Begin},
|
||||
%% BEGIN received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'BEGIN', received, TsmParms});
|
||||
_Other ->
|
||||
%% TID = no TID
|
||||
%% Build ABORT message (P-Abort Cause = Resource Limitation)
|
||||
Abort = {abort, #'Abort'{dtid = TPDU#'Begin'.otid,
|
||||
reason = {'p-abortCause', resourceLimitation}}},
|
||||
NewTPDU = list_to_binary('TCMessage':encode('TCMessage', Abort)),
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = UdataParms#'N-UNITDATA'.callingAddress,
|
||||
callingAddress = UdataParms#'N-UNITDATA'.calledAddress,
|
||||
sequenceControl = false, returnOption = false, importance = none,
|
||||
userData = NewTPDU},
|
||||
%% TR-UNI request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
error_logger:error_report(["Unable to create TSM for received N-BEGIN",
|
||||
{nsap, State#state.nsap}, {caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}])
|
||||
end,
|
||||
{noreply, State};
|
||||
{error, Reason} ->
|
||||
%% TODO
|
||||
%% is OTID derivable?
|
||||
%% Build ABORT message with appropraite P-Abort Cause value
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (4)
|
||||
error_logger:error_report(["Syntax error in received N-BEGIN", {error, Reason},
|
||||
{nsap, State#state.nsap}, {caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {continue, TPDU}} ->
|
||||
case 'TR':decode('Continue', TPDU) of
|
||||
{ok, Continue} ->
|
||||
%% DTID assigned?
|
||||
case catch ets:lookup_element(transaction, TPDU#'Continue'.dtid, 2) of
|
||||
{error, _Reason} ->
|
||||
error_logger:error_report(["DTID not found in received N-CONTINUE",
|
||||
{dtid, TPDU#'End'.dtid}, {nsap, State#state.nsap},
|
||||
{caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
%% TODO
|
||||
%% Build ABORT message with appropriate P-Abort Cause values
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (4)
|
||||
{noreply, State};
|
||||
TSM ->
|
||||
TsmParms = UdataParms#'N-UNITDATA'{userData = Continue},
|
||||
%% CONTINUE received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'CONTINUE', received, TsmParms}),
|
||||
{noreply, State}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
%% TODO
|
||||
%% OTID derivable?
|
||||
%% DTID assigned?
|
||||
%% Build ABORT message with appropraite P-Abort Cause value
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
%% Local Abort TSM <- TCO
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (2)
|
||||
error_logger:error_report(["Syntax error in received N-CONTINUE", {error, Reason},
|
||||
{nsap, State#state.nsap}, {caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {'end', TPDU}} ->
|
||||
case 'TR':decode('End', TPDU) of
|
||||
{ok, End} ->
|
||||
%% DTID assigned?
|
||||
case catch ets:lookup(transaction, TPDU#'End'.dtid, 2) of
|
||||
{error, _Reason} ->
|
||||
error_logger:error_report(["DTID not found in received N-END",
|
||||
{dtid, TPDU#'End'.dtid}, {nsap, State#state.nsap},
|
||||
{caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (3)
|
||||
{noreply, State};
|
||||
TSM ->
|
||||
TsmParms = UdataParms#'N-UNITDATA'{userData = End},
|
||||
%% END received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'END', received, TsmParms}),
|
||||
{noreply, State}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
%% TODO
|
||||
%% DTID assigned?
|
||||
%% Local Abort TSM <- TCO
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (5)
|
||||
error_logger:error_report(["Syntax error in received N-END", {error, Reason},
|
||||
{nsap, State#state.nsap}, {caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {abort, TPDU}} ->
|
||||
case 'TR':decode('Abort', TPDU) of
|
||||
{ok, Abort} ->
|
||||
%% DTID assigned?
|
||||
case catch ets:lookup(transaction, TPDU#'Abort'.dtid, 2) of
|
||||
{error, _Reason} ->
|
||||
error_logger:error_report(["DTID not found in received N-ABORT",
|
||||
{dtid, TPDU#'Abort'.dtid}, {nsap, State#state.nsap},
|
||||
{caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (3)
|
||||
{noreply, State};
|
||||
TSM ->
|
||||
TsmParms = UdataParms#'N-UNITDATA'{userData = Abort},
|
||||
%% Abort received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'ABORT', received, TsmParms}),
|
||||
{noreply, State}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
%% TODO
|
||||
%% DTID assigned?
|
||||
%% Local Abort TSM <- TCO
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (5)
|
||||
error_logger:error_report(["Syntax error in received N-ABORT", {error, Reason},
|
||||
{nsap, State#state.nsap}, {caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {error, Reason}} ->
|
||||
%% TODO
|
||||
%% Message type unknown
|
||||
%% OTID derivable?
|
||||
%% DTID assigned?
|
||||
%% Build ABORT message with appropraite P-Abort Cause value
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
%% Local Abort TSM <- TCO
|
||||
%% Discard received message
|
||||
%% reference: Figure A.3/Q/774 (sheet 4 of 4) label (2)
|
||||
error_logger:error_report(["Unknown TCMessage received", {error, Reason},
|
||||
{nsap, State#state.nsap}, {caller, UdataParms#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParms#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
|
||||
handle_cast({'N', 'NOTICE', indication, NoticeParms}, State) ->
|
||||
%% Extract the originating transactionID
|
||||
case 'TR':decode('TCMessage', NoticeParms#'N-NOTICE'.userData) of
|
||||
{ok, {'begin', TPDU}} ->
|
||||
case 'TR':decode('Begin', TPDU) of
|
||||
{ok, Begin} ->
|
||||
TransactionID = Begin#'Begin'.otid;
|
||||
_ ->
|
||||
TransactionID = undefined
|
||||
end;
|
||||
{ok, {continue, TPDU}} ->
|
||||
case 'TR':decode('Continue', TPDU) of
|
||||
{ok, Continue} ->
|
||||
TransactionID = Continue#'Continue'.otid;
|
||||
_ ->
|
||||
TransactionID = undefined
|
||||
end;
|
||||
_ ->
|
||||
TransactionID = undefined
|
||||
end,
|
||||
%% TR-NOTICE indication CSL <- TSL
|
||||
%% reference: Figure A.3/Q.774 (sheet 2 of 4)
|
||||
%% The CSL is a null layer for this indication so it becomes
|
||||
%% TC-NOTICE indication TCU <- TSL
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%% reference: Figure A.3/Q.774 (sheet 10 of 11)
|
||||
TcParms = #'TC-NOTICE'{
|
||||
dialogueID = TransactionID,
|
||||
origAddress = NoticeParms#'N-NOTICE'.callingAddress,
|
||||
destAddress = NoticeParms#'N-NOTICE'.calledAddress,
|
||||
reportCause = NoticeParms#'N-NOTICE'.reason},
|
||||
gen_fsm:send_event(State#state.usap, {'TC', 'NOTICE', indication, TcParms}),
|
||||
{noreply, State};
|
||||
|
||||
|
||||
%%%
|
||||
%%% service primitive requests from the TR-User
|
||||
%%% reference: Figure A.3/Q.774 (sheets 2&3 of 4)
|
||||
handle_cast({'TR', 'UNI', request, UniParms}, State)
|
||||
when is_record(UniParms, 'TR-UNI') ->
|
||||
%% Assemble TR-portion of UNI message
|
||||
{SequenceControl, ReturnOption, Importance} = UniParms#'TR-UNI'.qos,
|
||||
DialoguePortion = (UniParms#'TR-UNI'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (UniParms#'TR-UNI'.userData)#'TR-user-data'.componentPortion,
|
||||
TPDU = 'TR':encode('TCMessage', {unidirectional, #'Unidirectional'{
|
||||
dialoguePortion = DialoguePortion, components = ComponentPortion}}),
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = UniParms#'TR-UNI'.destAddress,
|
||||
callingAddress = UniParms#'TR-UNI'.origAddress,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = Importance, userData = TPDU},
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'BEGIN', request, BeginParms}, State)
|
||||
when is_record(BeginParms, 'TR-BEGIN') ->
|
||||
%% Create a Transaction State Machine (TSM)
|
||||
OTID = BeginParms#'TR-BEGIN'.transactionID,
|
||||
ChildName = list_to_atom("tsm_" ++ integer_to_list(OTID)),
|
||||
{ok, {M, F, A, Mods}} = application:get_env(start_tsm),
|
||||
StartFunc = {M, F, A ++ [{State#state.nsap, State#state.usap, OTID, ChildName}]},
|
||||
ChildSpec = {ChildName, StartFunc, temporary, infinity, worker, Mods},
|
||||
{ok, TSM} = supervisor:start_child(State#state.supervisor, ChildSpec),
|
||||
gen_fsm:send_event(TSM, {'BEGIN', transaction, BeginParms}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'CONTINUE', request, ContParms}, State)
|
||||
when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
TransactionID = ContParms#'TR-CONTINUE'.transactionID,
|
||||
TSM = ets:lookup_element(transaction, TransactionID, 2),
|
||||
gen_fsm:send_event(TSM, {'CONTINUE', transaction, ContParms}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'END', request, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
TransactionID = EndParms#'TR-END'.transactionID,
|
||||
TSM = ets:lookup_element(transaction, TransactionID, 2),
|
||||
gen_fsm:send_event(TSM, {'END', transaction, EndParms}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'U-ABORT', request, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
TransactionID = AbortParms#'TR-U-ABORT'.transactionID,
|
||||
TSM = ets:lookup_element(transaction, TransactionID, 2),
|
||||
gen_fsm:send_event(TSM, {'ABORT', transaction, AbortParms}),
|
||||
{noreply, State};
|
||||
|
||||
%%
|
||||
%% The TSM sends us a message as it's last action so
|
||||
%% we can remove the supervisor child specification
|
||||
%%
|
||||
handle_cast({'tsm-stopped', SupRef}, State) ->
|
||||
supervisor:delete_child(State#state.supervisor, SupRef),
|
||||
%% reference: Figure A.3/Q/774 (sheet 2 of 4)
|
||||
{noreply, State};
|
||||
|
||||
%% unrecognized casts
|
||||
handle_cast(Other, State) ->
|
||||
error_logger:error_report([{unknown_cast, Other}]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
%% trapped exit signals
|
||||
handle_info({'EXIT', _Pid, Reason}, State) ->
|
||||
{stop, Reason, State};
|
||||
|
||||
%% unknown messages
|
||||
handle_info(Unknown, State) ->
|
||||
error_logger:error_msg("Received unknown message: ~p~n", [Unknown]),
|
||||
{noreply, State}.
|
||||
|
||||
%% someone wants us to shutdown and cleanup
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%% upgrading the running code
|
||||
code_change(_, _, _) -> ok.
|
||||
|
||||
%%%
|
||||
%%% internal functions
|
||||
%%%
|
||||
|
||||
%% get the next originating transaction id from the global counter
|
||||
%%
|
||||
%% TODO: we are simply assuming that when the counter rolls over the last
|
||||
%% transaction to have this ID is long gone (4.2 billion IDs)
|
||||
%%
|
||||
%% reference: Figure A.3 bis/Q.774
|
||||
new_tid() ->
|
||||
ets:update_counter(transaction, transactionID, {2, 1, 16#ffffffff, 0}).
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
%%% $Id: tcap_tsm_fsm.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Transaction State Machine (TCM) functional block within the
|
||||
%%% transaction sub-layer of ANSI TCAP.
|
||||
%%%
|
||||
%%% @reference ANSI T1.114.4 Transaction Capabilities Procedures
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_tsm_fsm).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
|
||||
terminate/3, code_change/4]).
|
||||
|
||||
%% transaction_fsm state callbacks
|
||||
-export([idle/2, initiation_sent/2, initiation_received/2, active/2]).
|
||||
|
||||
%% record definitions for TR-User primitives
|
||||
-include("tcap.hrl").
|
||||
%% record definitions for N-User primitives
|
||||
-include("sccp.hrl").
|
||||
%% record definitions for TCAP messages
|
||||
-include("TCAPMessages.hrl").
|
||||
|
||||
%% the transaction_fsm state data
|
||||
-record(state, {nsap, usap, tco, supervisor, supref, localTID, remoteTID,
|
||||
local_address, remote_address, dha}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_fsm call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init({NSAP, USAP, TID, Supervisor, SupRef, TCO}) ->
|
||||
%% store our process identifier in the global transaction ID table
|
||||
ets:insert(transaction, {TID, self()}),
|
||||
process_flag(trap_exit, true),
|
||||
{ok, idle, #state{nsap = NSAP, usap = USAP, localTID = TID, supervisor = Supervisor,
|
||||
supref = SupRef, tco = TCO}}.
|
||||
|
||||
%%%
|
||||
%%% idle state handler
|
||||
%%%
|
||||
|
||||
%% started by remote
|
||||
%% reference: Figure A.4/Q.774 (sheet 1 of 5)
|
||||
idle({'BEGIN', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
%% Store remote address and remote TID
|
||||
NewState = State#state{remote_address = SccpParms#'N-UNITDATA'.callingAddress,
|
||||
remoteTID = (SccpParms#'N-UNITDATA'.userData)#'Begin'.otid},
|
||||
{ok, Begin} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
%% Start a Dialogue Handler (DHA)
|
||||
SupId = list_to_atom("dha_sup_" ++ integer_to_list(State#state.localTID)),
|
||||
StartFunc = {supervisor, start_link,
|
||||
[dialogue_sup, [{State#state.usap, State#state.localTID, self()}]]},
|
||||
ChildSpec = {SupId, StartFunc, permanent, infinity, supervisor, [dialogue_sup]},
|
||||
{ok, DHA} = supervisor:start_child(State#state.supervisor, ChildSpec),
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl, SccpParms#'N-UNITDATA'.returnOption},
|
||||
UserData = #'TR-user-data'{dialoguePortion = Begin#'Begin'.dialoguePortion,
|
||||
componentPortion = Begin#'Begin'.components},
|
||||
TrParms = #'TR-BEGIN'{qos = QOS,
|
||||
destAddress = SccpParms#'N-UNITDATA'.calledAddress,
|
||||
origAddress = SccpParms#'N-UNITDATA'.callingAddress,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
%% TR-BEGIN CSL <- TSL
|
||||
gen_fsm:send_event(DHA, {'TR', 'BEGIN', indication, TrParms}),
|
||||
{next_state, initiation_received, NewState#state{dha = DHA}};
|
||||
|
||||
%% started by TR-User
|
||||
%% reference: Figure A.4/Q.774 (sheet 1 of 5)
|
||||
idle({'BEGIN', transaction, BeginParms}, State)
|
||||
when is_record(BeginParms, 'TR-BEGIN') ->
|
||||
%% Store local address
|
||||
%% NOTE - This may be provided by TC-user or be implicitly associated with
|
||||
%% the access point at which the N-UNITDATA primitive is issued.
|
||||
NewState = State#state{local_address = BeginParms#'TR-BEGIN'.origAddress},
|
||||
DialoguePortion = (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.componentPortion,
|
||||
Begin = #'Begin'{otid = State#state.localTID, dialoguePortion = DialoguePortion,
|
||||
components = ComponentPortion},
|
||||
%% Assemble TR-portion of BEGIN message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {'begin', Begin})),
|
||||
{SequenceControl, ReturnOption} = BeginParms#'TR-BEGIN'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = BeginParms#'TR-BEGIN'.destAddress,
|
||||
callingAddress = BeginParms#'TR-BEGIN'.origAddress,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(NewState#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{next_state, initiation_sent, NewState}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% initiation_received state handler
|
||||
%%%
|
||||
%%% reference: Figure A.4/Q.774 (sheet 2 of 5)
|
||||
|
||||
%% Continue from TR-User
|
||||
initiation_received({'CONTINUE', transaction, ContParms}, State)
|
||||
when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
%% Store new local address if it is provided by User
|
||||
case ContParms#'TR-CONTINUE'.origAddress of
|
||||
undefined ->
|
||||
NewState = State;
|
||||
NewAddress ->
|
||||
NewState = State#state{local_address = NewAddress}
|
||||
end,
|
||||
DialoguePortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.componentPortion,
|
||||
Continue = #'Continue'{otid = State#state.localTID, dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of CONTINUE message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {continue, Continue})),
|
||||
{SequenceControl, ReturnOption} = ContParms#'TR-CONTINUE'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = NewState#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(NewState#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{next_state, active, NewState};
|
||||
|
||||
%% End from TR-User (prearranged)
|
||||
initiation_received({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END'),
|
||||
EndParms#'TR-END'.termination == prearranged ->
|
||||
{stop, normal, State};
|
||||
%% End from TR-User (not prearranged)
|
||||
initiation_received({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
DialoguePortion = (EndParms#'TR-END'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion,
|
||||
End = #'End'{dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of END message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {'end', End})),
|
||||
{SequenceControl, ReturnOption} = EndParms#'TR-END'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from TR-User
|
||||
initiation_received({'ABORT', transaction, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
Cause = (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion,
|
||||
Abort = #'Abort'{reason = {'u-abortCause', Cause}},
|
||||
%% Assemble TR-portion of ABORT message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {abort, Abort})),
|
||||
{SequenceControl, ReturnOption} = AbortParms#'TR-U-ABORT'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% initiation_sent state handler
|
||||
%%%
|
||||
%%% reference: Figure A.4/Q.774 (sheet 2 of 5)
|
||||
|
||||
%% Continue from remote
|
||||
initiation_sent({'CONTINUE', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
%% Store remote address and remote TID
|
||||
OTID = (SccpParms#'N-UNITDATA'.userData)#'Begin'.otid,
|
||||
NewState = State#state{ remote_address
|
||||
= SccpParms#'N-UNITDATA'.callingAddress, remoteTID = OTID},
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Continue} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = Continue#'Continue'.dialoguePortion,
|
||||
componentPortion = Continue #'Continue'.components},
|
||||
TrParms = #'TR-CONTINUE'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(NewState#state.dha, {'TR', 'CONTINUE', indication, TrParms}),
|
||||
{next_state, active, NewState};
|
||||
|
||||
%% End from remote
|
||||
initiation_sent({'END', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, End} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = End#'End'.dialoguePortion,
|
||||
componentPortion = End#'End'.components},
|
||||
TrParms = #'TR-END'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'END', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from remote
|
||||
initiation_sent({'ABORT', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Abort} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
%% TR-U-ABORT?
|
||||
case Abort#'Abort'.reason of
|
||||
{'p-abortCause', Cause} ->
|
||||
TrParms = #'TR-P-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
pAbort = Cause},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms});
|
||||
{'u-abortCause', Cause} ->
|
||||
UserData = #'TR-user-data'{dialoguePortion = Cause},
|
||||
TrParms = #'TR-U-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'U-ABORT', indication, TrParms})
|
||||
end,
|
||||
{stop, normal, State};
|
||||
|
||||
%% Local Abort
|
||||
initiation_sent({'local-abort', received, Cause}, State) ->
|
||||
TrParms = #'TR-P-ABORT'{pAbort = Cause},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% End from TR-User
|
||||
initiation_sent({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from TR-User
|
||||
initiation_sent({'ABORT', transaction, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
%% Purely local action
|
||||
{stop, normal, State}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% active state handler
|
||||
%%%
|
||||
%%% reference: Figure A.4/Q.774 (sheet 2 of 5)
|
||||
|
||||
%% Continue received from remote
|
||||
active({'CONTINUE', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Continue} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = Continue#'Continue'.dialoguePortion,
|
||||
componentPortion = Continue#'Continue'.components},
|
||||
TrParms = #'TR-CONTINUE'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'CONTINUE', indication, TrParms}),
|
||||
{next_state, active, State};
|
||||
|
||||
%% Continue from TR-User
|
||||
active({'CONTINUE', transaction, ContParms}, State)
|
||||
when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
DialoguePortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.componentPortion,
|
||||
Continue = #'Continue'{dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of CONTINUE message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {continue, Continue})),
|
||||
{SequenceControl, ReturnOption} = ContParms#'TR-CONTINUE'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{next_state, active, State};
|
||||
|
||||
%% End from remote
|
||||
active({'END', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, End} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = End#'End'.dialoguePortion,
|
||||
componentPortion = End#'End'.components},
|
||||
TrParms = #'TR-END'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
%% TR-END indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'END', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% End from TR-User (prearranged)
|
||||
active({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END'),
|
||||
EndParms#'TR-END'.termination == prearranged ->
|
||||
{stop, normal, State};
|
||||
%% End from TR-User (not prearranged)
|
||||
active({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
DialoguePortion = (EndParms#'TR-END'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion,
|
||||
End = #'End'{dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of END message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {'end', End})),
|
||||
{SequenceControl, ReturnOption} = EndParms#'TR-END'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort received from remote
|
||||
active({'ABORT', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Abort} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
%% TR-U-ABORT?
|
||||
case Abort#'Abort'.reason of
|
||||
{'p-abortCause', Cause} -> % No
|
||||
TrParms = #'TR-P-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
pAbort = Cause},
|
||||
%% TR-P-ABORT indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms});
|
||||
{'u-abortCause', Cause} -> % Yes
|
||||
UserData = #'TR-user-data'{dialoguePortion = Cause},
|
||||
TrParms = #'TR-U-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
%% TR-U-ABORT indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'U-ABORT', indication, TrParms})
|
||||
end,
|
||||
{stop, normal, State};
|
||||
|
||||
%% Local Abort
|
||||
active({'local-abort', received, Cause}, State) ->
|
||||
TrParms = #'TR-P-ABORT'{qos = {false, false},
|
||||
transactionID = State#state.localTID, pAbort= Cause},
|
||||
%% TR-P-ABORT indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from TR-User
|
||||
active({'ABORT', transaction, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
Cause = (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion,
|
||||
Abort = #'Abort'{reason = {'u-abortCause', Cause}},
|
||||
%% Assemble TR-portion of ABORT message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {abort, Abort})),
|
||||
{SequenceControl, ReturnOption} = AbortParms#'TR-U-ABORT'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State}.
|
||||
|
||||
|
||||
%% handle an event sent using gen_fsm:send_all_state_event/2
|
||||
handle_event(Event, StateName, State) ->
|
||||
error_logger:format("transaction_fsm (~w) received unexpected message: ~w~n", [Event]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle an event sent using gen_fsm:sync_send_all_state_event/2,3
|
||||
handle_sync_event(Event, _From, StateName, State) ->
|
||||
error_logger:format("transaction_fsm (~w) received unexpected message: ~w~n", [Event]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle any other message
|
||||
handle_info(Info, StateName, State) ->
|
||||
error_logger:format("transaction_fsm (~w) received unexpected message: ~w~n", [Info]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle a shutdown request
|
||||
terminate(_Reason, _StateName, State) ->
|
||||
ets:delete(transaction, State#state.localTID),
|
||||
%% signal TCO that we are stopping
|
||||
gen_server:cast(State#state.supervisor, {'tsm-stopped', State#state.supref}).
|
||||
|
||||
%% handle updating state data due to a code replacement
|
||||
code_change(_OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
## $id$
|
||||
|
||||
SCCPINCDIR = ../../../SCCP/itu/include
|
||||
|
||||
EBIN = ../../ebin/itu
|
||||
|
||||
ERL = erl
|
||||
ERLC = erlc
|
||||
EMULATOR = beam
|
||||
ERLCFLAGS = -b ${EMULATOR} -o ${EBIN} -W -v +warn_unused_vars -I ../../asn_src/itu -I ../../include -I ${SCCPINCDIR}
|
||||
|
||||
${EBIN}/%.${EMULATOR}:%.erl
|
||||
${ERLC} ${ERLCFLAGS} $<
|
||||
|
||||
BEAMS = ${EBIN}/tcap_tco_server.${EMULATOR} \
|
||||
${EBIN}/tcap_tsm_fsm.${EMULATOR} \
|
||||
${EBIN}/tcap_dha_fsm.${EMULATOR} \
|
||||
${EBIN}/tcap_cco_server.${EMULATOR} \
|
||||
${EBIN}/tcap_ism_fsm.${EMULATOR}
|
||||
|
||||
.PHONY: default
|
||||
default: all
|
||||
|
||||
.PHONY: all
|
||||
all: ${BEAMS}
|
||||
|
||||
.PHONY: install
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
- rm -f ${BEAMS}
|
||||
|
||||
tcap_tco_server.${EMULATOR}: tcap_tco_server.erl \
|
||||
../../asn_src/TCAPMessages.hrl ../../include/tcap.hrl \
|
||||
${SCCPINCDIR}/sccp.hrl
|
||||
tcap_tsm_fsm.${EMULATOR}: tcap_tsm_fsm.erl \
|
||||
../../asn_src/TCAPMessages.hrl ../../include/tcap.hrl \
|
||||
${SCCPINCDIR}/sccp.hrl
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
%%% $Id: tcap_cco_server.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc TCAP Component Coordinator (CCO) functional block within the
|
||||
%%% component sub-layer of ITU TCAP.
|
||||
%%%
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_cco_server).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% call backs needed for gen_server behaviour
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {supervisor, usap, dialogueID}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init([Supervisor, USAP, DialogueID]) ->
|
||||
process_flag(trap_exit, true),
|
||||
{ok, #state{supervisor = Supervisor, usap = USAP, dialogueID = DialogueID}}.
|
||||
|
||||
%% shutdown the server
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, shutdown, ok, State};
|
||||
|
||||
%% unrecognized calls
|
||||
handle_call(Other, From, State) ->
|
||||
error_logger:error_report([{unknown_call, Other}, {from, From}]),
|
||||
{noreply, State}.
|
||||
|
||||
%% unrecognized casts
|
||||
handle_cast(Other, State) ->
|
||||
error_logger:error_report([{unknown_cast, Other}]),
|
||||
{noreply, State}.
|
||||
|
||||
|
||||
%% trapped exit signals
|
||||
handle_info({'EXIT', _Pid, Reason}, State) ->
|
||||
{stop, Reason, State};
|
||||
|
||||
%% unknown messages
|
||||
handle_info(Unknown, State) ->
|
||||
error_logger:error_msg("Received unknown message: ~p~n", [Unknown]),
|
||||
{noreply, State}.
|
||||
|
||||
%% someone wants us to shutdown and cleanup
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
%% upgrading the running code
|
||||
code_change(_, _, _) -> ok.
|
||||
|
||||
%%%
|
||||
%%% internal functions
|
||||
%%%
|
||||
|
|
@ -0,0 +1,807 @@
|
|||
%%% $Id: tcap_dha_fsm.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Dialogue Handler (DHA) functional block within the component
|
||||
%%% sub-layer of ITU TCAP.
|
||||
%%%
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_dha_fsm).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_info/3, handle_event/3, handle_sync_event/4,
|
||||
terminate/3, code_change/4]).
|
||||
|
||||
%% transaction_fsm state callbacks
|
||||
-export([idle/2, wait_for_uni_components/2, wait_for_begin_components/2,
|
||||
initiation_received/2, wait_cont_components_ir/2,
|
||||
wait_cont_components_active/2, wait_for_end_components/2,
|
||||
initiation_sent/2, active/2]).
|
||||
|
||||
%% record definitions for TR-User primitives
|
||||
-include("tcap.hrl").
|
||||
%% record definitions for N-User primitives
|
||||
-include("sccp.hrl").
|
||||
%% record definitions for TCAP messages
|
||||
%-include("TCAPMessages.hrl").
|
||||
%-include("UnidialoguePDUs.hrl").
|
||||
%-include("DialoguePDUs.hrl").
|
||||
|
||||
%% the dialogue_fsm state data
|
||||
-record(state, {usap, tco, supid, cco, otid, did, parms, appContextMode}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_fsm call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% Start the Dialogue Handler (DHA) process
|
||||
%% reference: Figure A.5/Q.774 (sheet 1 of 11)
|
||||
init({USAP, DialogueID, TCO, Supervisor}) ->
|
||||
init({USAP, DialogueID, TCO, undefined, Supervisor});
|
||||
init({USAP, DialogueID, TCO, SupId, Supervisor}) ->
|
||||
%% Start a Component Coordinator (CCO) process
|
||||
ChildName = list_to_atom("cco_sup_" ++ integer_to_list(DialogueID)),
|
||||
StartFunc = {supervisor, start_link, [component_coordinator_sup, [USAP, DialogueID]]},
|
||||
ChildSpec = {ChildName, StartFunc, permanent, infinity,
|
||||
supervisor, [component_coordinator_sup]},
|
||||
{ok, CCO} = supervisor:start_child(Supervisor, ChildSpec),
|
||||
process_flag(trap_exit, true),
|
||||
{ok, idle, #state{usap = USAP, did = DialogueID,
|
||||
tco = TCO, supid = SupId, cco = CCO}}.
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 1 of 11)
|
||||
%%% TC-UNI request from TCU
|
||||
idle({'TC', 'UNI', request, UniParms}, State)
|
||||
when is_record(UniParms, 'TC-UNI') ->
|
||||
%% Dialogue info included?
|
||||
case UniParms#'TC-UNI'.userInfo of
|
||||
undefined ->
|
||||
DialoguePortion = undefined;
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
%% Build AUDT apdu
|
||||
DialoguePortion = 'UnidialoguePDUs':encode('AUDT-apdu',
|
||||
#'AUDT-apdu'{'application-context-name' = UniParms#'TC-UNI'.appContextName,
|
||||
'user-information' = UserInfo})
|
||||
end,
|
||||
TrParms = #'TR-UNI'{qos = UniParms#'TC-UNI'.qos,
|
||||
destAddress = UniParms#'TC-UNI'.destAddress,
|
||||
origAddress = UniParms#'TC-UNI'.origAddress,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms},
|
||||
%% Request components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_uni_components, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 1 of 11)
|
||||
%%% TC-BEGIN request from TCU
|
||||
idle({'TC', 'BEGIN', request, BeginParms}, State)
|
||||
when is_record(BeginParms, 'TC-BEGIN') ->
|
||||
%% Dialogue info included?
|
||||
case BeginParms#'TC-BEGIN'.userInfo of
|
||||
undefined ->
|
||||
DialoguePortion = undefined;
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
%% Set protocol version = 1
|
||||
%% Build AARQ apdu
|
||||
DialoguePortion = 'DialoguePDUs':encode('AARQ-apdu',
|
||||
#'AARQ-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = BeginParms#'TC-BEGIN'.appContextName,
|
||||
'user-information' = UserInfo})
|
||||
end,
|
||||
TrParms = #'TR-BEGIN'{qos = BeginParms#'TC-BEGIN'.qos,
|
||||
destAddress = BeginParms#'TC-BEGIN'.destAddress,
|
||||
origAddress = BeginParms#'TC-BEGIN'.origAddress,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms,
|
||||
%% Set application context mode
|
||||
appContextMode = BeginParms#'TC-BEGIN'.appContextName},
|
||||
%% Request components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_begin_components, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 2 of 11)
|
||||
%%% TR-UNI indication from TSL
|
||||
idle({'TR', 'UNI', indication, UniParms}, State) when is_record(UniParms, 'TR-UNI') ->
|
||||
%% Extract dialogue portion
|
||||
case extract_uni_dialogue_portion(UniParms#'TR-UNI'.userData) of
|
||||
incorrect_dialogue_portion -> %% Dialogue portion correct? (no)
|
||||
%% Discard components
|
||||
{stop, normal, State};
|
||||
no_version1 -> %% Is version 1 supported? (no)
|
||||
%% Discard components
|
||||
{stop, normal, State};
|
||||
TcParms when is_record(TcParms, 'TC-UNI') ->
|
||||
if
|
||||
is_record(UniParms#'TR-UNI'.userData, 'TR-user-data'),
|
||||
(UniParms#'TR-UNI'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (UniParms#'TR-UNI'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Assign dialogue ID
|
||||
DialogueID = transaction_coordinator_server:new_tid(),
|
||||
NewTcParms = TcParms#'TC-UNI'{qos = UniParms#'TR-UNI'.qos,
|
||||
destAddress = UniParms#'TR-UNI'.destAddress,
|
||||
origAddress = UniParms#'TR-UNI'.origAddress,
|
||||
dialogueID = DialogueID,
|
||||
componentsPresent = ComponentsPresent},
|
||||
NewState = State#state{did = DialogueID, parms = NewTcParms},
|
||||
%% Components to CHA
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok % should never happen
|
||||
end,
|
||||
%% TC-UNI indication to TCU
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'UNI', indication, NewTcParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 3 of 11)
|
||||
%%% TR-BEGIN indication from TSL
|
||||
idle({'TR', 'BEGIN', indication, BeginParms}, State) when is_record(BeginParms, 'TR-BEGIN') ->
|
||||
%% Extract dialogue portion
|
||||
case extract_begin_dialogue_portion(BeginParms#'TR-BEGIN'.userData) of
|
||||
incorrect_dialogue_portion -> %% Dialogue portion correct? (no)
|
||||
%% Build ABORT apdu
|
||||
ABRT = 'DialoguePDUs':encode('ABRT-apdu', #'ABRT-apdu'{'abort-source' = 'dialogue-service-provider'}),
|
||||
%% Discard components
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = {transactionID = BeginParms#'TR-BEGIN'.transactionID,
|
||||
userData = #'TR-user-data'{dialoguePortion = ABRT}},
|
||||
NewState = State#state{otid = BeginParms#'TR-BEGIN'.transactionID, parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
no_version1 -> %% Is version 1 supported? (no)
|
||||
DialoguePortion = (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.dialoguePortion,
|
||||
%% Build AARE apdu
|
||||
AARE = 'DialoguePDUs':encode('AARE-apdu', #'AARE-apdu'{
|
||||
'protocol-version' = version1,
|
||||
'application-context-name' = DialoguePortion#'AARQ-apdu'.'application-context-name',
|
||||
result = reject-permanent,
|
||||
'result-source-diagnostic' = {'dialogue-service-provider', 'no-common-dialogue-portion'}}),
|
||||
%% Discard components
|
||||
%% TR-P-ABORT request to TSL
|
||||
TrParms = {transactionID = BeginParms#'TR-P-ABORT'.transactionID, pAbort = AARE},
|
||||
NewState = State#state{otid = BeginParms#'TR-BEGIN'.transactionID,
|
||||
appContextMode = DialoguePortion#'AARQ-apdu'.'application-context-name',
|
||||
parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'P-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
TcParms when is_record(TcParms, 'TC-BEGIN') ->
|
||||
if
|
||||
is_record(BeginParms#'TR-BEGIN'.userData, 'TR-user-data'),
|
||||
(BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Assign dialogue ID
|
||||
DialogueID = transaction_coordinator_server:new_tid(),
|
||||
NewTcParms = TcParms#'TC-BEGIN'{qos = BeginParms#'TR-BEGIN'.qos,
|
||||
destAddress = BeginParms#'TR-BEGIN'.destAddress,
|
||||
origAddress = BeginParms#'TR-BEGIN'.origAddress,
|
||||
dialogueID = DialogueID,
|
||||
componentsPresent = ComponentsPresent},
|
||||
NewState = State#state{otid = BeginParms#'TR-BEGIN'.transactionID, did = DialogueID,
|
||||
parms = NewTcParms, appContextMode = TcParms#'TC-BEGIN'.appContextName},
|
||||
%% TC-BEGIN indication to TCU
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'BEGIN', indication, NewTcParms}),
|
||||
%% Any components?
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
%% Components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
{next_state, initiation_received, NewState}
|
||||
end.
|
||||
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
%%% TC-CONTINUE request from TCU
|
||||
initiation_received({'TC', 'CONTINUE', request, ContParms}, State) when is_record(ContParms, 'TC-CONTINUE') ->
|
||||
%% Dialogue info included?
|
||||
case ContParms#'TC-CONTINUE'.userInfo of
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
AARE = #'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = ContParms#'TC-CONTINUE'.appContextName,
|
||||
result = accepted,
|
||||
'result-source-diagnostic' = {'dialogue-service-user', null},
|
||||
'user-information' = UserInfo},
|
||||
DialoguePortion = 'DialoguePDUs':encode('AARE-apdu', AARE),
|
||||
TrParms = #'TR-CONTINUE'{qos = ContParms#'TC-CONTINUE'.qos,
|
||||
origAddress = ContParms#'TR-CONTINUE'.origAddress,
|
||||
transactionID = State#state.otid,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms};
|
||||
undefined ->
|
||||
NewState = State
|
||||
end,
|
||||
{next_state, wait_cont_components_ir, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
%%% TC-END request from TCU
|
||||
initiation_received({'TC', 'END', request, EndParms}, State) when is_record(EndParms, 'TC-END') ->
|
||||
%% Prearranged end?
|
||||
case EndParms#'TC-END'.termination of
|
||||
prearranged ->
|
||||
%% TR-END request to TSL
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'END', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
basic ->
|
||||
%% Dialogue info included?
|
||||
case EndParms#'TC-END'.userInfo of
|
||||
UserInfo when is_binary(UserInfo) ->
|
||||
AARE = #'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = EndParms#'TC-END'.appContextName,
|
||||
result = accepted,
|
||||
'result-source-diagnostic' = {'dialogue-service-user', null},
|
||||
'user-information' = UserInfo},
|
||||
DialoguePortion = 'DialoguePDUs':encode('AARE-apdu', AARE),
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination,
|
||||
userData = #'TR-user-data'{dialoguePortion = DialoguePortion}},
|
||||
NewState = State#state{parms = TrParms};
|
||||
undefined ->
|
||||
NewState = State
|
||||
end,
|
||||
%% Request components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_end_components, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 6 of 11)
|
||||
%%% TC-U-ABORT request from TCU
|
||||
initiation_received({'TC', 'U-ABORT', request, AbortParms}, State) when is_record(AbortParms, 'TC-U-ABORT'),
|
||||
(AbortParms#'TC-U-ABORT'.abortReason == applicationContextNotSupported)
|
||||
or (AbortParms#'TC-U-ABORT'.abortReason == dialogueRefused)
|
||||
or (AbortParms#'TC-U-ABORT'.abortReason == userSpecified) ->
|
||||
case State#state.appContextMode of
|
||||
%% Is application context mode set? (no)
|
||||
undefined ->
|
||||
UserData = #'TR-user-data'{};
|
||||
%% Abort reason present and = AC-name not supported OR dialogue refused?
|
||||
_AppContextName when AbortParms#'TC-U-ABORT'.abortReason == applicationContextNotSupported ->
|
||||
%% Set protocol version = 1
|
||||
%% Build AARE-pdu (rejected)
|
||||
AARE = 'DialoguePDUs':encode('AARE-apdu',
|
||||
#'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = AbortParms#'TC-U-ABORT'.appContextName,
|
||||
result = 'reject-permanent',
|
||||
'result-source-diagnostic' = {'dialogue-service-user', 'application-context-name-not-supported'}}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = AARE};
|
||||
_AppContextName when AbortParms#'TC-U-ABORT'.abortReason == dialogueRefused ->
|
||||
%% Set protocol version = 1
|
||||
%% Build AARE-pdu (rejected)
|
||||
AARE = 'DialoguePDUs':encode('AARE-apdu',
|
||||
#'AARE-apdu'{'protocol-version' = version1,
|
||||
'application-context-name' = AbortParms#'TC-U-ABORT'.appContextName,
|
||||
result = 'reject-permanent',
|
||||
'result-source-diagnostic' = {'dialogue-service-user', null}}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = AARE};
|
||||
_AppContextName when AbortParms#'TC-U-ABORT'.abortReason == userSpecified ->
|
||||
%% Build ABRT-apdu (abort source = dialogue-service-user)
|
||||
ABRT = 'DialoguePDUs':encode('ABRT-apdu',
|
||||
#'ABRT-apdu'{'abort-source' = 'dialogue-service-user',
|
||||
'user-information' = AbortParms#'TC-U-ABORT'.userInfo}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = ABRT}
|
||||
end,
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = #'TR-U-ABORT'{qos = AbortParms#'TC-U-ABORT'.qos,
|
||||
transactionID = State#state.otid,
|
||||
userData = UserData},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}.
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%%% TC-END request from TCU
|
||||
initiation_sent({'TC', 'END', request, EndParms}, State) when is_record(EndParms, 'TC-END'),
|
||||
EndParms#'TC-END'.termination == prearranged -> % termination must be prearranged
|
||||
%% TR-END request to TSL
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'END', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%%% TC-U-ABORT request from TCU (local action)
|
||||
initiation_sent({'TC', 'U-ABORT', request, AbortParms}, State) when is_record(AbortParms, 'TC-U-ABORT') ->
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = #'TR-U-ABORT'{qos = AbortParms#'TC-U-ABORT'.qos, transactionID = State#state.otid},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
{stop, normal, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%%% TR-END indication from TSL
|
||||
initiation_sent({'TR', 'END', indication, EndParms}, State) when is_record(EndParms, 'TR-END') ->
|
||||
if
|
||||
is_record(EndParms#'TR-END'.userData, 'TR-user-data'),
|
||||
(EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Dialogue portion included?
|
||||
%% AC Mode set?
|
||||
%% Extract dialogue portion
|
||||
%% Dialogue portion correct?
|
||||
case extract_dialogue_portion(EndParms#'TR-END'.userData, State#state.appContextMode) of
|
||||
abort ->
|
||||
%% Discard components
|
||||
%% TC-P-ABORT indication to TCU
|
||||
TcParms = #'TC-P-ABORT'{qos = EndParms#'TR-END'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
AARE ->
|
||||
%% TC-END indication to TCU
|
||||
TcParms = #'TC-END'{qos = EndParms#'TR-END'.qos,
|
||||
dialogueID = State#state.did,
|
||||
appContextName = State#state.appContextMode,
|
||||
componentsPresent = ComponentsPresent,
|
||||
userInfo = AARE,
|
||||
termination = EndParms#'TR-END'.termination},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'END', indication, TcParms}),
|
||||
%% Any components?
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
%% Components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
%% NOTE: currently the TCO short circuits this function and sends directly to TCU
|
||||
initiation_sent({'TR', 'NOTICE', indication, NoticeParms}, State) when is_record(NoticeParms, 'TR-NOTICE') ->
|
||||
%% TC-NOTICE indication to TCU
|
||||
TcParms = #'TC-NOTICE'{dialogueID = State#state.did,
|
||||
origAddress = NoticeParms#'TR-NOTICE'.origAddress,
|
||||
destAddress = NoticeParms#'TR-NOTICE'.destAddress,
|
||||
reportCause = NoticeParms#'TR-NOTICE'.reportCause},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'NOTICE', indication, TcParms}),
|
||||
{next_state, initiation_sent, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 8 of 11)
|
||||
%% TR-CONTINUE indication from TSL
|
||||
initiation_sent({'TR', 'CONTINUE', indication, ContParms}, State) when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
if
|
||||
is_record(ContParms#'TR-CONTINUE'.userData, 'TR-user-data'),
|
||||
(ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.componentPortion /= undefined ->
|
||||
case 'TC':decode('ComponentPortion', (ContParms#'TR-END'.userData)#'TR-user-data'.componentPortion) of
|
||||
[] = Components -> ComponentsPresent = false;
|
||||
Components -> ComponentsPresent = true
|
||||
end;
|
||||
true ->
|
||||
Components = undefined,
|
||||
ComponentsPresent = false
|
||||
end,
|
||||
%% Dialogue portion included?
|
||||
%% AC Mode set?
|
||||
%% Extract dialogue portion
|
||||
%% Dialogue portion correct?
|
||||
case extract_dialogue_portion(ContParms#'TR-CONTINUE'.userData, State#state.appContextMode) of
|
||||
abort ->
|
||||
%% Discard components
|
||||
%% TC-P-ABORT indication to TCU
|
||||
TcParms = #'TC-P-ABORT'{qos = ContParms#'TR-CONTINUE'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms}),
|
||||
%% Build ABRT apdu
|
||||
ABRT = 'DialoguePDUs':encode('ABRT-apdu',
|
||||
#'ABRT-apdu'{'abort-source' = 'dialogue-service-provider'}),
|
||||
UserData = #'TR-user-data'{dialoguePortion = ABRT},
|
||||
%% TR-U-ABORT request to TSL
|
||||
TrParms = #'TR-U-ABORT'{qos = ContParms#'TC-U-ABORT'.qos,
|
||||
transactionID = NewState#state.otid, userData = UserData},
|
||||
LastState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(LastState#state.tco, {'TR', 'U-ABORT', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(LastState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, LastState};
|
||||
AARE ->
|
||||
%% TC-CONTINUE indication to TCU
|
||||
TcParms = #'TC-CONTINUE'{qos = ContParms#'TR-CONTINUE'.qos,
|
||||
origAddress = ContParms#'TR-CONTINUE'.origAddress,
|
||||
appContextName = State#state.appContextMode,
|
||||
dialogueID = State#state.did,
|
||||
userInfo = AARE,
|
||||
componentsPresent = ComponentsPresent},
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'END', indication, TcParms}),
|
||||
%% Any components?
|
||||
case ComponentsPresent of
|
||||
true ->
|
||||
%% Components to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, {components, Components});
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
{next_state, active, NewState}
|
||||
end;
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 8 of 11)
|
||||
%% TR-U-ABORT indication from TSL
|
||||
initiation_sent({'TR', 'U-ABORT', indication, AbortParms}, State) when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
case catch begin
|
||||
if
|
||||
%% Is AC mode set? (no) Is Dialogue portion present? (no)
|
||||
State#state.appContextMode == undefined and (not is_record(AbortParms#'TR-U-ABORT'.userData, 'TR-user-data')
|
||||
or (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion == undefined) ->
|
||||
throw(#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos, dialogueID = State#state.did});
|
||||
%% Is AC mode set? (no) Is Dialogue portion present? (yes)
|
||||
State#state.appContextMode == undefined, is_record(AbortParms#'TR-U-ABORT'.userData, 'TR-user-data'),
|
||||
AbortParms#'TR-U-ABORT'.userData /= undefined ->
|
||||
throw(#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did, pAbort = abnormalDialogue});
|
||||
%% Is User Data included in primitive? (no)
|
||||
not is_record(AbortParms#'TR-U-ABORT'.userData, 'TR-user-data');
|
||||
(AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion == undefined ->
|
||||
throw(#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did, pAbort = abnormalDialogue});
|
||||
true -> ok
|
||||
end,
|
||||
%% Is PDU type = ABRT or AARE (rejected)?
|
||||
case 'DialoguePDUs':decode('DialoguePDU', (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion) of
|
||||
%% Is abstract syntax = dialogue-PDU AS? (no)
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'),
|
||||
APDU#'AARE-apdu'.'application-context-name' /= State#state.appContextMode ->
|
||||
#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue};
|
||||
%% Is Abort source = user? (yes)
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'ABRT-apdu'),
|
||||
element(1, APDU#'ABRT-apdu'.'abort-source') == 'dialogue-service-user' ->
|
||||
#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
abortReason = userSpecific,
|
||||
userInfo = APDU#'ABRT-apdu'.'user-information'};
|
||||
%% Is Associate source = user? (yes)
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'), APDU#'AARE-apdu'.'result-source-diagnostic'
|
||||
== {'dialogue-service-user', 'application-context-name-not-supported'},
|
||||
APDU#'AARE-apdu'.result == 'reject-permanent' ->
|
||||
#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
abortReason = applicationContextNotSupported,
|
||||
appContextName = APDU#'AARE-apdu'.'application-context-name',
|
||||
userInfo = APDU#'AARE-apdu'.'user-information'};
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'),
|
||||
element(1, APDU#'AARE-apdu'.'result-source-diagnostic') == 'dialogue-service-user',
|
||||
APDU#'AARE-apdu'.result == 'reject-permanent' ->
|
||||
#'TC-U-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
abortReason = dialogueRefused,
|
||||
appContextName = APDU#'AARE-apdu'.'application-context-name',
|
||||
userInfo = APDU#'AARE-apdu'.'user-information'};
|
||||
%% Is AARE (no common dialogue portion)?
|
||||
{dialoguePDU, APDU} when is_record(APDU, 'AARE-apdu'),
|
||||
APDU#'AARE-apdu'.'result-source-diagnostic' == {'dialogue-service-provider', 'no-common-dialogue-portion'} ->
|
||||
#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = noCommonDialoguePortion};
|
||||
_ ->
|
||||
#'TC-P-ABORT'{qos = AbortParms#'TR-U-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = abnormalDialogue}
|
||||
end
|
||||
end of
|
||||
TcParms when is_record(TcParms, 'TC-U-ABORT') ->
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'U-ABORT', indication, TcParms});
|
||||
TcParms when is_record(TcParms, 'TC-P-ABORT') ->
|
||||
NewState = State#state{parms = TcParms},
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms})
|
||||
end,
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 8 of 11)
|
||||
%% TR-P-ABORT indication from TSL
|
||||
initiation_sent({'TR', 'P-ABORT', indication, AbortParms}, State) when is_record(AbortParms, 'TR-P-ABORT') ->
|
||||
TcParms = #'TC-P-ABORT'{qos = AbortParms#'TR-P-ABORT'.qos,
|
||||
dialogueID = State#state.did,
|
||||
pAbort = AbortParms#'TR-P-ABORT'.pAbort},
|
||||
NewState = State#state{parms = TcParms},
|
||||
%% TC-P-ABORT indication to TCU
|
||||
gen_fsm:send_event(NewState#state.usap, {'TC', 'P-ABORT', indication, TcParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState}.
|
||||
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
%% TC-CONTINUE request from TCU
|
||||
active({'TC', 'CONTINUE', request, ContParms}, State) when is_record(ContParms, 'TC-CONTINUE') ->
|
||||
NewState = State#state{parms = ContParms},
|
||||
%% Request component to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_cont_components_active, NewState};
|
||||
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
%% TC-END request from TCU
|
||||
active({'TC', 'END', request, EndParms}, State) when is_record(EndParms, 'TC-END') ->
|
||||
%% Prearranged end?
|
||||
case EndParms#'TC-END'.termination of
|
||||
prearranged ->
|
||||
%% TR-END request to TSL
|
||||
TrParms = #'TR-END'{qos = EndParms#'TC-END'.qos,
|
||||
transactionID = State#state.otid,
|
||||
termination = EndParms#'TC-END'.termination},
|
||||
NewState = State#state{parms = TrParms},
|
||||
gen_fsm:send_event(NewState#state.tco, {'TR', 'END', request, TrParms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(NewState#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, NewState};
|
||||
basic ->
|
||||
%% Request component to CHA
|
||||
gen_fsm:send_event(State#state.cco, 'request-components'),
|
||||
%% Process components
|
||||
{next_state, wait_for_end_components, State}
|
||||
end.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 2 of 11)
|
||||
wait_for_uni_components('no-component', State) ->
|
||||
wait_for_uni_components1(State);
|
||||
wait_for_uni_components({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-UNI'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-UNI'{userData = NewUserData},
|
||||
wait_for_uni_components1(State#state{parms = TrParms}).
|
||||
wait_for_uni_components1(State) ->
|
||||
%% TR-UNI request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'UNI', request, State#state.parms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(State#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, State}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 2 of 11)
|
||||
wait_for_begin_components('no-component', State) ->
|
||||
wait_for_begin_components1(State);
|
||||
wait_for_begin_components({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-BEGIN'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-BEGIN'{userData = NewUserData},
|
||||
wait_for_begin_components1(State#state{parms = TrParms}).
|
||||
wait_for_begin_components1(State) ->
|
||||
%% Assign local transaction ID
|
||||
TrParms = (State#state.parms)#'TR-BEGIN'{transactionID = transaction_coordinator_server:new_tid()},
|
||||
%% TR-BEGIN request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'BEGIN', request, TrParms}),
|
||||
{next_state, initiation_sent, State#state{parms = TrParms}}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
wait_cont_components_ir('no-component', State) ->
|
||||
wait_cont_components_ir1(State);
|
||||
wait_cont_components_ir({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-CONTINUE'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-CONTINUE'{userData = NewUserData},
|
||||
wait_cont_components_ir1(State#state{parms = TrParms}).
|
||||
wait_cont_components_ir1(State) ->
|
||||
%% TR-CONTINUE request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'CONTINUE', request, State#state.parms}),
|
||||
{next_state, initiation_sent, State}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
wait_cont_components_active('no-component', State) ->
|
||||
wait_cont_components_active1(State);
|
||||
wait_cont_components_active({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-CONTINUE'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-CONTINUE'{userData = NewUserData},
|
||||
wait_cont_components_active1(State#state{parms = TrParms}).
|
||||
wait_cont_components_active1(State) ->
|
||||
%% TR-CONTINUE request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'CONTINUE', request, State#state.parms}),
|
||||
{next_state, active, State}.
|
||||
|
||||
%% reference: Figure A.5 bis/Q.774
|
||||
%% reference: Figure A.5/Q.774 (sheet 5 of 11)
|
||||
%% reference: Figure A.5/Q.774 (sheet 9 of 11)
|
||||
wait_for_end_components('no-component', State) ->
|
||||
wait_for_end_components1(State);
|
||||
wait_for_end_components({'requested-components', Components}, State) ->
|
||||
%% Assemble component portion
|
||||
ComponentPortion = 'TC':encode('ComponentPortion', Components),
|
||||
%% Assemble TSL user data
|
||||
UserData = (State#state.parms)#'TR-END'.userData,
|
||||
NewUserData = UserData#'TR-user-data'{componentPortion = ComponentPortion},
|
||||
TrParms = (State#state.parms)#'TR-END'{userData = NewUserData},
|
||||
wait_for_end_components1(State#state{parms = TrParms}).
|
||||
wait_for_end_components1(State) ->
|
||||
%% TR-END request to TSL
|
||||
gen_fsm:send_event(State#state.tco, {'TR', 'END', request, State#state.parms}),
|
||||
%% Dialogue terminated to CHA
|
||||
gen_fsm:send_event(State#state.cco, 'dialogue-terminated'),
|
||||
%% Free dialogue ID
|
||||
{stop, normal, State}.
|
||||
|
||||
%% Dialogue portion included? (yes)
|
||||
extract_uni_dialogue_portion(UserData) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Dialogue portion correct?
|
||||
case 'UnidialoguePDUs':decode('UnidialoguePDU', UserData#'TR-user-data'.dialoguePortion) of
|
||||
{unidialoguePDU, AUDT} when is_record(AUDT, 'AUDT-apdu') ->
|
||||
%% Is version 1 supported?
|
||||
case lists:member(version1, AUDT#'AUDT-apdu'.'protocol-version') of
|
||||
true ->
|
||||
#'TC-UNI'{appContextName = AUDT#'AUDT-apdu'.'application-context-name',
|
||||
userInfo = AUDT#'AUDT-apdu'.'user-information'};
|
||||
false ->
|
||||
no_version1
|
||||
end;
|
||||
_ ->
|
||||
incorrect_dialogue_portion
|
||||
end;
|
||||
%% Dialogue portion included? (no)
|
||||
extract_uni_dialogue_portion(_DialoguePortion) ->
|
||||
#'TC-UNI'{}.
|
||||
|
||||
|
||||
%% Dialogue portion included? (yes)
|
||||
extract_begin_dialogue_portion(UserData) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Dialogue portion correct?
|
||||
case 'DialoguePDUs':decode('DialoguePDU', UserData#'TR-user-data'.dialoguePortion) of
|
||||
{dialoguePDU, AARQ} when is_record(AARQ, 'AARQ-apdu') ->
|
||||
%% Is version 1 supported?
|
||||
case lists:member(version1, AARQ#'AARQ-apdu'.'protocol-version') of
|
||||
true ->
|
||||
%% Set application context mode
|
||||
#'TC-BEGIN'{appContextName = AARQ#'AARQ-apdu'.'application-context-name',
|
||||
userInfo = AARQ#'AARQ-apdu'.'user-information'};
|
||||
false ->
|
||||
no_version1
|
||||
end;
|
||||
_ ->
|
||||
incorrect_dialogue_portion
|
||||
end;
|
||||
%% Dialogue portion included? (no)
|
||||
extract_begin_dialogue_portion(_DialoguePortion) ->
|
||||
#'TC-BEGIN'{}.
|
||||
|
||||
extract_dialogue_portion(UserData, undefined) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Dialogue portion included? (yes) AC mode set? (no)
|
||||
abort;
|
||||
extract_dialogue_portion(UserData, _AppContextName) when not is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion == undefined ->
|
||||
%% Dialogue portion included? (no) AC mode set? (yes)
|
||||
abort;
|
||||
extract_dialogue_portion(UserData, _AppContextName) when is_record(UserData, 'TR-user-data'),
|
||||
UserData#'TR-user-data'.dialoguePortion /= undefined ->
|
||||
%% Extract dialogue portion
|
||||
case 'DialoguePDUs':decode('DialoguePDU', UserData#'TR-user-data'.dialoguePortion) of
|
||||
{dialoguePDU, AARE} when is_record(AARE, 'AARE-apdu') ->
|
||||
AARE; %% Dialogue portion correct? (yes)
|
||||
_ ->
|
||||
abort %% Dialogue portion correct? (no)
|
||||
end.
|
||||
|
||||
|
||||
%% handle any other message
|
||||
handle_info(Info, StateName, State) ->
|
||||
error_logger:format("dialogue_fsm (~w) received unexpected message: ~w~n", [Info]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle an event sent using gen:fsm_send_all_state_event/2
|
||||
handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%% handle an event sent using gen_fsm:sync_send_all_state_event/2,3
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
%% handle a shutdown request
|
||||
terminate(_Reason, _StateName, State) when State#state.supid == undefined ->
|
||||
%% we were started by TSM, no worries
|
||||
ok;
|
||||
terminate(_Reason, _StateName, State) ->
|
||||
%% signal TCO so he can reap the ChildSpec of our supervisor
|
||||
gen_server:cast(State#state.tco, {'dha-stopped', State#state.supid}).
|
||||
|
||||
%% handle updating state data due to a code replacement
|
||||
code_change(_OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
%%% $Id: tcap_ism_fsm.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Invocation State Machine (ISM) functional block within the
|
||||
%%% component sub-layer of ITU TCAP.
|
||||
%%%
|
||||
%%% @reference ANSI T1.114.4 Transaction Capabilities Procedures
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_ism_fsm).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_event/3, handle_info/3, terminate/3, code_change/4]).
|
||||
|
||||
%% invocation_fsm state callbacks
|
||||
-export([]).
|
||||
|
||||
%% record definitions for TC-User primitives
|
||||
-include("tcap.hrl").
|
||||
%% record definitions for TCAP messages
|
||||
%-include("TCAPMessages.hrl").
|
||||
|
||||
%% the invocation_fsm state data
|
||||
-record(state, {usap, dialogueID, cco}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_fsm call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% Start the Invocation State Machine (ISM) process
|
||||
%% reference: Figure A.7/Q.774 (sheet 1 of 6)
|
||||
|
||||
|
||||
%% handle any other message
|
||||
handle_info(Info, StateName, State) ->
|
||||
error_logger:format("~w (~w) received unexpected message: ~w~n", [?Module, self(), Event]),
|
||||
{next_state, StateName, State};
|
||||
|
||||
%% handle a shutdown request
|
||||
terminate(_Reason, _StateName, State) -> ok.
|
||||
|
||||
%% handle updating state data due to a code replacement
|
||||
code_change(OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -0,0 +1,696 @@
|
|||
%%% $Id: tcap_tco_server.erl,v 1.7 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Transaction Coordinator (TCO) functional block within the
|
||||
%%% transaction sub-layer of ITU TCAP.
|
||||
%%%
|
||||
%%% <p>This module implements the transaction coordinator (TCO)
|
||||
%%% functional block. Adaptations to specific SCCP layer
|
||||
%%% and TC-User implementations may be implemented as callback
|
||||
%%% modules behaving to this behaviour module. This module behaves
|
||||
%%% to <tt>gen_server</tt>.</p>
|
||||
%%%
|
||||
%%% <h2>Usage</h2>
|
||||
%%% <p>The callback module should be implemented as a gen_server
|
||||
%%% behaviour but with a <tt>tcap_tco_server</tt> behaviour
|
||||
%%% module attribute:
|
||||
%%% <pre>
|
||||
%%% -behaviour(tcap_tco_server).</pre></p>
|
||||
%%%
|
||||
%%% <p>The call back module handles the SCCP -> TCAP primitives
|
||||
%%% directly, performs any reformatting required, and returns the
|
||||
%%% standard primitives to the <tt>tcap_tco_server</tt> handler. A
|
||||
%%% very simple example is the
|
||||
%%% <a href="http://www.motivity.ca/sccp"><tt>sccp</tt></a>
|
||||
%%% application which implements the SCCP SAP as a pid() which sends
|
||||
%%% and receives messages in the primitive format. In our callback
|
||||
%%% module we create a <tt>handle_info/2</tt> clause which matches
|
||||
%%% the primitives:</p>
|
||||
%%%
|
||||
%%% <p><pre>handle_info({'N', _, indication, _} = Primitive, State) ->
|
||||
%%% {primitive, Primitive, State}.</pre>
|
||||
%%% As the example above illustrates the <tt>tcap_tco_server</tt>
|
||||
%%% behaviour extends the allowed return values to accept the direct
|
||||
%%% return of a received service primitive.</p>
|
||||
%%%
|
||||
%%% <p>The <tt>handle_cast/2</tt> function may be used in the same
|
||||
%%% way.</p>
|
||||
%%%
|
||||
%%% <h2><a name="calbacks">Callback Functions</a></h2>
|
||||
%%%
|
||||
%%% <p>In addition to the <tt>gen_server</tt> callbacks the following
|
||||
%%% callback functions are used.</p>
|
||||
%%%
|
||||
%%% <h3><a name="send_primitive-2">send_primitive/2</a></h3>
|
||||
%%%
|
||||
%%% <p><tt>send_primitive(Primitive, State) -> void()</tt>
|
||||
%%% <ul><li><tt>Primitive = {'N', 'UNITDATA', request, UdataParams}</tt></li>
|
||||
%%% <li><tt>UdataParams = #'N-UNITDATA'{}</tt></li>
|
||||
%%% </ul>
|
||||
%%% The TCO will call this function when it has a service primitive
|
||||
%%% to deliver to the SCCP layer.</p>
|
||||
%%%
|
||||
%%% <h3><a name="start_user-2">start_user/2</a></h3>
|
||||
%%%
|
||||
%%% <p><tt>start_user(CSL, DialogueID, State) -> pid()</tt>
|
||||
%%% <ul><li><tt>CSL = {DHA, CCO}</tt></li>
|
||||
%%% <li><tt>DHA = pid()</tt></li>
|
||||
%%% <li><tt>CCO = pid()</tt></li>
|
||||
%%% <li><tt>DialogueID = tid()</tt></li>
|
||||
%%% </ul>
|
||||
%%% This function is called by a dialogue handler (DHA) to initialize
|
||||
%%% a local TC-User for a dialogue begun by a remote TC-User.</p>
|
||||
%%% <p><tt>CSL</tt> is the component sublayer identifier which
|
||||
%%% contains the pids of the dialogue handler and component coordinator.</p>
|
||||
%%% <p>Returns the pid of the TC-User process whcih will handle the
|
||||
%%% new dialogue.</p>
|
||||
%%%
|
||||
%%% <h3><a name="start_transaction-1">start_transaction/2</a></h3>
|
||||
%%%
|
||||
%%% <p><tt>start_transaction(TransactionID, State) -> StartFunc</tt>
|
||||
%%% <ul><li><tt>TransactionID = tid()</tt></li>
|
||||
%%% <li><tt>State = term()</tt></li>
|
||||
%%% <li><tt>StartFunc = {M,F,A}</tt></li>
|
||||
%%% <li><tt>M = F = atom()</tt></li>
|
||||
%%% <li><tt>A = [term()]</tt></li>
|
||||
%%% </ul>
|
||||
%%% The callback module may optionally export this function
|
||||
%%% to overide the default method used to start a transaction
|
||||
%%% state machine (TSM).</p>
|
||||
%%% <p>StartFunc defines the function call used to start the TSM
|
||||
%%% process. It should be a module-function-arguments tuple
|
||||
%%% <tt>{M,F,A}</tt> used as <tt>apply(M,F,A)</tt>.</p>
|
||||
%%% <p>The start function must create and link to the child process,
|
||||
%%% and should return <tt>{ok, Child}</tt> where <tt>Child</tt> is
|
||||
%%% the pid of the child process.</p>
|
||||
%%% <p>See the description of StartFunc in the supervisor module.</p>
|
||||
%%%
|
||||
%%% <h3><a name="start_dialogue-1">start_dialogue/1</a></h3>
|
||||
%%%
|
||||
%%% <p><tt>start_dialogue(DialogueID, State) -> StartFunc</tt>
|
||||
%%% <ul><li><tt>DialogueID = tid()</tt></li>
|
||||
%%% <li><tt>State = term()</tt></li>
|
||||
%%% <li><tt>StartFunc = {M,F,A}</tt></li>
|
||||
%%% <li><tt>M = F = atom()</tt></li>
|
||||
%%% <li><tt>A = [term()]</tt></li>
|
||||
%%% </ul>
|
||||
%%% The callback module may optionally export this function
|
||||
%%% to overide the default method used to start a dialogue
|
||||
%%% handler (DHA).</p>
|
||||
%%% <p>StartFunc defines the function call used to start the DHA
|
||||
%%% process. It should be a module-function-arguments tuple
|
||||
%%% <tt>{M,F,A}</tt> used as <tt>apply(M,F,A)</tt>.</p>
|
||||
%%% <p>The start function must create and link to the child process,
|
||||
%%% and should return <tt>{ok, Child}</tt> where <tt>Child</tt> is
|
||||
%%% the pid of the child process.</p>
|
||||
%%% <p>See the description of StartFunc in the supervisor module.</p>
|
||||
%%%
|
||||
%%% @end
|
||||
%%%
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
|
||||
-module(tcap_tco_server).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.7 $').
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
% export the gen_server interface
|
||||
-export([start/3, start/4, start_link/3, start_link/4,
|
||||
call/2, call/3, multi_call/2, multi_call/3, multi_call/4,
|
||||
cast/2, abcast/2, abcast/3, reply/2, enter_loop/4, enter_loop/5]).
|
||||
|
||||
% export the gen_server call backs
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
% behaviour modules must export this function
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
%% define what callbacks users must export
|
||||
%%
|
||||
%% @hidden
|
||||
behaviour_info(callbacks) ->
|
||||
gen_server:behaviour_info(callbacks)
|
||||
% add the tcap_tco_server required callbacks
|
||||
++ [{send_primitive, 2}, {start_transaction, 2}, {start_dialogue, 2}];
|
||||
behaviour_info(Other) ->
|
||||
gen_server:behaviour_info(Other).
|
||||
|
||||
-include("TCAPMessages.hrl").
|
||||
-include("tcap.hrl").
|
||||
-include("sccp.hrl").
|
||||
|
||||
-record(state, {supervisor, module, ext_state}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% @hidden
|
||||
init([Sup, Module, Args]) ->
|
||||
process_flag(trap_exit, true),
|
||||
case Module:init(Args) of
|
||||
{ok, ExtState} ->
|
||||
NewState = #state{supervisor = Sup, module = Module, ext_state = ExtState},
|
||||
{ok, NewState};
|
||||
{ok, ExtState, Timeout} ->
|
||||
NewState = #state{supervisor = Sup, module = Module, ext_state = ExtState},
|
||||
{ok, NewState, Timeout};
|
||||
{stop, Reason} ->
|
||||
{stop, Reason};
|
||||
ignore ->
|
||||
ignore;
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%% @hidden
|
||||
%%
|
||||
% assign a new dialogue ID
|
||||
handle_call(dialogueID, From, State) ->
|
||||
{reply, new_tid(), State};
|
||||
% shutdown the server
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, shutdown, ok, State};
|
||||
% unknown request
|
||||
handle_call(Request, From, State) ->
|
||||
Module = State#state.module,
|
||||
case Module:handle_call(Request, From, State#state.ext_state) of
|
||||
{reply, Reply, ExtState} ->
|
||||
{reply, Reply, State#state{ext_state = ExtState}};
|
||||
{reply, Reply, ExtState, Timeout} ->
|
||||
{reply, Reply, State#state{ext_state = ExtState}, Timeout};
|
||||
{noreply, ExtState} ->
|
||||
{noreply, State#state{ext_state = ExtState}};
|
||||
{noreply, ExtState, Timeout} ->
|
||||
{noreply, State#state{ext_state = ExtState}, Timeout};
|
||||
{stop, Reason, Reply, ExtState} ->
|
||||
{stop, Reason, Reply, State#state{ext_state = ExtState}};
|
||||
{stop, Reason, ExtState} ->
|
||||
{stop, Reason, State#state{ext_state = ExtState}};
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%% @spec (Request, State) -> Result
|
||||
%% Info = term()
|
||||
%% State = term()
|
||||
%% Result = {noreply, NewState} | {noreply, NewState, Timeout}
|
||||
%% | {stop, Reason, NewState}
|
||||
%% | {primitive, Primitive, NewState}
|
||||
%% NewState = term()
|
||||
%% Timeout = int() | infinity
|
||||
%% Reason = term()
|
||||
%% Primitive = {'N', 'UNITDATA', indication, SccpParams} |
|
||||
%% {'N', 'NOTICE', indication, SccpParams}
|
||||
%%
|
||||
%% @doc Receive a message sent with <tt>gen_server:cast/2</tt>.
|
||||
%%
|
||||
%% <p>A user callback module may return an SCCP service primitive
|
||||
%% to TCO for processing with the return value
|
||||
%% <tt>{primitive, Primitive, NewState}</tt>.</p>
|
||||
%%
|
||||
%% @see //stdlib/gen_server:handle_cast/2
|
||||
%%
|
||||
%% @end
|
||||
%%
|
||||
% service primitive indications from the network layer
|
||||
%
|
||||
% reference: Figure A.3/Q.774 (sheet 1 of 4)
|
||||
handle_cast({'N', 'UNITDATA', indication, UdataParams}, State)
|
||||
when is_record(UdataParams, 'N-UNITDATA') ->
|
||||
case 'TR':decode('TCMessage', UdataParams#'N-UNITDATA'.userData) of
|
||||
{ok, {unidirectional, TPDU}} ->
|
||||
case 'TR':decode('Unidirectional', TPDU) of
|
||||
{ok, Unidirectional} ->
|
||||
% Create a Dialogue Handler (DHA)
|
||||
DialogueID = new_tid(),
|
||||
StartFunc = get_start(dialogue, DialogueID, State),
|
||||
ChildSpec = {DialogueID, StartFunc, temporary, 4000, worker, [tcap_dha_fsm]},
|
||||
{ok, DHA} = supervisor:start_child(State#state.supervisor, ChildSpec),
|
||||
% TR-UNI indication CSL <- TSL
|
||||
UserData = #'TR-user-data'{dialoguePortion = Unidirectional#'Unidirectional'.dialoguePortion,
|
||||
componentPortion = Unidirectional#'Unidirectional'.components},
|
||||
TrParams = #'TR-UNI'{qos =
|
||||
destAddress = UdataParams#'N-UNITDATA'.calledAddress,
|
||||
origAddress = UdataParams#'N-UNITDATA'.callingAddress,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(DHA, {'TR', 'UNI', indication, TrParams}),
|
||||
{noreply, State};
|
||||
{error, Reason} ->
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (3)
|
||||
error_logger:error_report(["Syntax error in received N-UNI", {error, Reason},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {'begin', TPDU}} ->
|
||||
case 'TR':decode('Begin', TPDU) of
|
||||
{ok, Begin} ->
|
||||
% Assign local transaction ID
|
||||
TransactionID = new_tid(),
|
||||
StartFunc = get_start(transaction, TransactionID, State),
|
||||
ChildSpec = {TransactionID, StartFunc, temporary, infinity, supervisor, [tcap_tsm_fsm]},
|
||||
% Is TID = no TID?
|
||||
% Note: The assignment of the ID above just gets the next available
|
||||
% value and doesn't ensure that it is not in use (unlikely)
|
||||
% or that there are enough resources available. The real
|
||||
% test is in whether the start succeeds.
|
||||
case supervisor:start_child(State#state.supervisor, ChildSpec) of
|
||||
{ok, TSM} ->
|
||||
% Created a Transaction State Machine (TSM)
|
||||
TsmParams = UdataParams#'N-UNITDATA'{userData = Begin},
|
||||
% BEGIN received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'BEGIN', received, TsmParams});
|
||||
_Other ->
|
||||
% TID = no TID
|
||||
% Build ABORT message (P-Abort Cause = Resource Limitation)
|
||||
Abort = {abort, #'Abort'{dtid = TPDU#'Begin'.otid,
|
||||
reason = {'p-abortCause', resourceLimitation}}},
|
||||
NewTPDU = list_to_binary('TCMessage':encode('TCMessage', Abort)),
|
||||
SccpParams = #'N-UNITDATA'{calledAddress = UdataParams#'N-UNITDATA'.callingAddress,
|
||||
callingAddress = UdataParams#'N-UNITDATA'.calledAddress,
|
||||
sequenceControl = false, returnOption = false, importance = none,
|
||||
userData = NewTPDU},
|
||||
% TR-UNI request TSL -> SCCP
|
||||
Module = State#state.module,
|
||||
Module:send_primitive({'N', 'UNITDATA', request, SccpParams}, State#state.ext_state),
|
||||
error_logger:error_report(["Unable to create TSM for received N-BEGIN",
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}])
|
||||
end,
|
||||
{noreply, State};
|
||||
{error, Reason} ->
|
||||
% TODO
|
||||
% is OTID derivable?
|
||||
% Build ABORT message with appropraite P-Abort Cause value
|
||||
% N-UNITDATA request TSL -> SCCP
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (4)
|
||||
error_logger:error_report(["Syntax error in received N-BEGIN", {error, Reason},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {continue, TPDU}} ->
|
||||
case 'TR':decode('Continue', TPDU) of
|
||||
{ok, Continue} ->
|
||||
% DTID assigned?
|
||||
case catch ets:lookup_element(transaction, TPDU#'Continue'.dtid, 2) of
|
||||
{error, _Reason} ->
|
||||
error_logger:error_report(["DTID not found in received N-CONTINUE",
|
||||
{dtid, TPDU#'End'.dtid},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
% TODO
|
||||
% Build ABORT message with appropriate P-Abort Cause values
|
||||
% N-UNITDATA request TSL -> SCCP
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (4)
|
||||
{noreply, State};
|
||||
TSM ->
|
||||
TsmParams = UdataParams#'N-UNITDATA'{userData = Continue},
|
||||
% CONTINUE received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'CONTINUE', received, TsmParams}),
|
||||
{noreply, State}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
% TODO
|
||||
% OTID derivable?
|
||||
% DTID assigned?
|
||||
% Build ABORT message with appropraite P-Abort Cause value
|
||||
% N-UNITDATA request TSL -> SCCP
|
||||
% Local Abort TSM <- TCO
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (2)
|
||||
error_logger:error_report(["Syntax error in received N-CONTINUE", {error, Reason},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {'end', TPDU}} ->
|
||||
case 'TR':decode('End', TPDU) of
|
||||
{ok, End} ->
|
||||
% DTID assigned?
|
||||
case catch ets:lookup(transaction, TPDU#'End'.dtid, 2) of
|
||||
{error, _Reason} ->
|
||||
error_logger:error_report(["DTID not found in received N-END",
|
||||
{dtid, TPDU#'End'.dtid},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (3)
|
||||
{noreply, State};
|
||||
TSM ->
|
||||
TsmParams = UdataParams#'N-UNITDATA'{userData = End},
|
||||
% END received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'END', received, TsmParams}),
|
||||
{noreply, State}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
% TODO
|
||||
% DTID assigned?
|
||||
% Local Abort TSM <- TCO
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (5)
|
||||
error_logger:error_report(["Syntax error in received N-END", {error, Reason},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {abort, TPDU}} ->
|
||||
case 'TR':decode('Abort', TPDU) of
|
||||
{ok, Abort} ->
|
||||
% DTID assigned?
|
||||
case catch ets:lookup(transaction, TPDU#'Abort'.dtid, 2) of
|
||||
{error, _Reason} ->
|
||||
error_logger:error_report(["DTID not found in received N-ABORT",
|
||||
{dtid, TPDU#'Abort'.dtid},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (3)
|
||||
{noreply, State};
|
||||
TSM ->
|
||||
TsmParams = UdataParams#'N-UNITDATA'{userData = Abort},
|
||||
% Abort received TSM <- TCO
|
||||
gen_fsm:send_event(TSM, {'ABORT', received, TsmParams}),
|
||||
{noreply, State}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
% TODO
|
||||
% DTID assigned?
|
||||
% Local Abort TSM <- TCO
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (5)
|
||||
error_logger:error_report(["Syntax error in received N-ABORT", {error, Reason},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
{ok, {error, Reason}} ->
|
||||
% TODO
|
||||
% Message type unknown
|
||||
% OTID derivable?
|
||||
% DTID assigned?
|
||||
% Build ABORT message with appropraite P-Abort Cause value
|
||||
% N-UNITDATA request TSL -> SCCP
|
||||
% Local Abort TSM <- TCO
|
||||
% Discard received message
|
||||
% reference: Figure A.3/Q/774 (sheet 4 of 4) label (2)
|
||||
error_logger:error_report(["Unknown TCMessage received", {error, Reason},
|
||||
{caller, UdataParams#'N-UNITDATA'.callingAddress},
|
||||
{called, UdataParams#'N-UNITDATA'.calledAddress}]),
|
||||
{noreply, State}
|
||||
end;
|
||||
|
||||
handle_cast({'N', 'NOTICE', indication, NoticeParams}, State) ->
|
||||
% Extract the originating transactionID
|
||||
case 'TR':decode('TCMessage', NoticeParams#'N-NOTICE'.userData) of
|
||||
{ok, {'begin', TPDU}} ->
|
||||
case 'TR':decode('Begin', TPDU) of
|
||||
{ok, Begin} ->
|
||||
TransactionID = Begin#'Begin'.otid;
|
||||
_ ->
|
||||
TransactionID = undefined
|
||||
end;
|
||||
{ok, {continue, TPDU}} ->
|
||||
case 'TR':decode('Continue', TPDU) of
|
||||
{ok, Continue} ->
|
||||
TransactionID = Continue#'Continue'.otid;
|
||||
_ ->
|
||||
TransactionID = undefined
|
||||
end;
|
||||
_ ->
|
||||
TransactionID = undefined
|
||||
end,
|
||||
% TR-NOTICE indication CSL <- TSL
|
||||
% reference: Figure A.3/Q.774 (sheet 2 of 4)
|
||||
% The CSL is a null layer for this indication so it becomes
|
||||
% TC-NOTICE indication TCU <- TSL
|
||||
% reference: Figure A.5/Q.774 (sheet 7 of 11)
|
||||
% reference: Figure A.3/Q.774 (sheet 10 of 11)
|
||||
TcParams = #'TC-NOTICE'{
|
||||
dialogueID = TransactionID,
|
||||
origAddress = NoticeParams#'N-NOTICE'.callingAddress,
|
||||
destAddress = NoticeParams#'N-NOTICE'.calledAddress,
|
||||
reportCause = NoticeParams#'N-NOTICE'.reason},
|
||||
% TODO: fixme!!! gen_fsm:send_event(State#state.usap, {'TC', 'NOTICE', indication, TcParams}),
|
||||
{noreply, State};
|
||||
|
||||
|
||||
%%
|
||||
%% service primitive requests from the TR-User
|
||||
%% reference: Figure A.3/Q.774 (sheets 2&3 of 4)
|
||||
handle_cast({'TR', 'UNI', request, UniParams}, State)
|
||||
when is_record(UniParams, 'TR-UNI') ->
|
||||
% Assemble TR-portion of UNI message
|
||||
{SequenceControl, ReturnOption, Importance} = UniParams#'TR-UNI'.qos,
|
||||
DialoguePortion = (UniParams#'TR-UNI'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (UniParams#'TR-UNI'.userData)#'TR-user-data'.componentPortion,
|
||||
TPDU = 'TR':encode('TCMessage', {unidirectional, #'Unidirectional'{
|
||||
dialoguePortion = DialoguePortion, components = ComponentPortion}}),
|
||||
SccpParams = #'N-UNITDATA'{calledAddress = UniParams#'TR-UNI'.destAddress,
|
||||
callingAddress = UniParams#'TR-UNI'.origAddress,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = Importance, userData = TPDU},
|
||||
Module = State#state.module,
|
||||
Module:send_primitive({'N', 'UNITDATA', request, SccpParams}, State#state.ext_state),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'BEGIN', request, BeginParams}, State)
|
||||
when is_record(BeginParams, 'TR-BEGIN') ->
|
||||
% Create a Transaction State Machine (TSM)
|
||||
OTID = BeginParams#'TR-BEGIN'.transactionID,
|
||||
ChildName = list_to_atom("tsm_" ++ integer_to_list(OTID)),
|
||||
{ok, {M, F, A, Mods}} = application:get_env(start_tsm),
|
||||
% TDODO: fixme!!! StartFunc = get_start(transaction, OTID, State),
|
||||
% ChildSpec = {ChildName, StartFunc, temporary, infinity, worker, [tcap_tsm_fsm]},
|
||||
% {ok, TSM} = supervisor:start_child(State#state.supervisor, ChildSpec),
|
||||
% gen_fsm:send_event(TSM, {'BEGIN', transaction, BeginParams}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'CONTINUE', request, ContParams}, State)
|
||||
when is_record(ContParams, 'TR-CONTINUE') ->
|
||||
TransactionID = ContParams#'TR-CONTINUE'.transactionID,
|
||||
TSM = ets:lookup_element(transaction, TransactionID, 2),
|
||||
gen_fsm:send_event(TSM, {'CONTINUE', transaction, ContParams}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'END', request, EndParams}, State)
|
||||
when is_record(EndParams, 'TR-END') ->
|
||||
TransactionID = EndParams#'TR-END'.transactionID,
|
||||
TSM = ets:lookup_element(transaction, TransactionID, 2),
|
||||
gen_fsm:send_event(TSM, {'END', transaction, EndParams}),
|
||||
{noreply, State};
|
||||
handle_cast({'TR', 'U-ABORT', request, AbortParams}, State)
|
||||
when is_record(AbortParams, 'TR-U-ABORT') ->
|
||||
TransactionID = AbortParams#'TR-U-ABORT'.transactionID,
|
||||
TSM = ets:lookup_element(transaction, TransactionID, 2),
|
||||
gen_fsm:send_event(TSM, {'ABORT', transaction, AbortParams}),
|
||||
{noreply, State};
|
||||
|
||||
%
|
||||
% The TSM sends us a message as it's last action so
|
||||
% we can remove the supervisor child specification
|
||||
%
|
||||
handle_cast({'tsm-stopped', SupRef}, State) ->
|
||||
supervisor:delete_child(State#state.supervisor, SupRef),
|
||||
% reference: Figure A.3/Q/774 (sheet 2 of 4)
|
||||
{noreply, State};
|
||||
|
||||
% unrecognized request
|
||||
handle_cast(Request, State) ->
|
||||
Module = State#state.module,
|
||||
case Module:handle_cast(Request, State#state.ext_state) of
|
||||
{noreply, ExtState} ->
|
||||
{noreply, State#state{ext_state = ExtState}};
|
||||
{noreply, ExtState, Timeout} ->
|
||||
{noreply, State#state{ext_state = ExtState}, Timeout};
|
||||
{primitive, Primitive, ExtState} ->
|
||||
handle_cast(Primitive, State#state{ext_state = ExtState});
|
||||
{stop, Reason, ExtState} ->
|
||||
{stop, Reason, State#state{ext_state = ExtState}};
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%% @spec (Info, State) -> Result
|
||||
%% Info = timeout | term()
|
||||
%% State = term()
|
||||
%% Result = {noreply, NewState} | {noreply, NewState, Timeout}
|
||||
%% | {stop, Reason, NewState}
|
||||
%% | {primitive, Primitive, NewState}
|
||||
%% NewState = term()
|
||||
%% Timeout = int() | infinity
|
||||
%% Reason = term()
|
||||
%% Primitive = {'N', 'UNITDATA', indication, SccpParams} |
|
||||
%% {'N', 'NOTICE', indication, SccpParams}
|
||||
%%
|
||||
%% @doc Receive a message sent with '!'.
|
||||
%%
|
||||
%% <p>A user callback module may return an SCCP service primitive
|
||||
%% to TCO for processing with the return value
|
||||
%% <tt>{primitive, Primitive, NewState}</tt>.</p>
|
||||
%%
|
||||
%% @see //stdlib/gen_server:handle_info/2
|
||||
%%
|
||||
handle_info({'EXIT', _Pid, Reason}, State) ->
|
||||
{stop, Reason, State};
|
||||
handle_info(Info, State) ->
|
||||
Module = State#state.module,
|
||||
case Module:handle_info(Info, State#state.ext_state) of
|
||||
{noreply, ExtState} ->
|
||||
{noreply, State#state{ext_state = ExtState}};
|
||||
{noreply, ExtState, Timeout} ->
|
||||
{noreply, State#state{ext_state = ExtState}, Timeout};
|
||||
{primitive, Primitive, ExtState} ->
|
||||
handle_cast(Primitive, State#state{ext_state = ExtState});
|
||||
{stop, Reason, ExtState} ->
|
||||
{stop, Reason, State#state{ext_state = ExtState}};
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%% @hidden
|
||||
terminate(Reason, State) ->
|
||||
Module = State#state.module,
|
||||
Module:terminate(Reason, State#state.ext_state).
|
||||
|
||||
%% @hidden
|
||||
code_change(OldVersion, statename, State, Extra) ->
|
||||
Module = State#state.module,
|
||||
case Module:code_change(OldVersion, State#state.ext_state, Extra) of
|
||||
{ok, ExtState} ->
|
||||
{ok, State#state{ext_state = ExtState}};
|
||||
Other ->
|
||||
Other
|
||||
end.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% internal functions
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% @hidden
|
||||
%%
|
||||
%% get the next originating transaction id from the global counter
|
||||
%%
|
||||
%% NOTE: we are simply assuming that when the counter rolls over the last
|
||||
%% transaction to have this ID is long gone (4.2 billion IDs)
|
||||
%%
|
||||
%% reference: Figure A.3 bis/Q.774
|
||||
new_tid() ->
|
||||
ets:update_counter(transaction, transactionID, {2, 1, 16#ffffffff, 0}).
|
||||
|
||||
get_start(dialogue, DialogueID, State) ->
|
||||
Module = State#state.module,
|
||||
case erlang:function_exported(Module, start_dialogue, 1) of
|
||||
true ->
|
||||
Module:start_dialogue(DialogueID, State#state.ext_state);
|
||||
false ->
|
||||
StartUserFun = fun(CSL) -> Module:start_user(CSL, DialogueID, State#state.ext_state) end,
|
||||
StartArgs = [DialogueID, self(), StartUserFun],
|
||||
{gen_fsm, start_link, [tcap_dha_fsm, StartArgs, []]}
|
||||
end;
|
||||
get_start(in_transaction, TransactionID, State) ->
|
||||
Module = State#state.module,
|
||||
case erlang:function_exported(Module, start_transaction, 1) of
|
||||
true ->
|
||||
Module:start_transaction(TransactionID, State#state.ext_state);
|
||||
false ->
|
||||
SendFun = fun(P) -> Module:send_primitive(P, State#state.ext_state) end,
|
||||
StartDHA = get_start(dialogue, TransactionID, State),
|
||||
StartArgs = [TransactionID, SendFun, StartDHA],
|
||||
{gen_fsm, start_link, [tcap_dha_fsm, StartArgs, []]}
|
||||
end;
|
||||
get_start(out_transaction, TransactionID, State) ->
|
||||
Module = State#state.module,
|
||||
case erlang:function_exported(Module, start_transaction, 1) of
|
||||
true ->
|
||||
Module:start_transaction(TransactionID, State#state.ext_state);
|
||||
false ->
|
||||
SendFun = fun(P) -> Module:send_primitive(P, State#state.ext_state) end,
|
||||
StartDHA = get_start(dialogue, TransactionID, State),
|
||||
StartArgs = [TransactionID, SendFun, StartDHA],
|
||||
{gen_fsm, start_link, [tcap_dha_fsm, StartArgs, []]}
|
||||
end.
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_server API functions
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% @hidden
|
||||
start(Module, Args, Options) ->
|
||||
gen_server:start(?MODULE, [Module, Args], Options).
|
||||
|
||||
%% @hidden
|
||||
start(ServerRef, Module, Args, Options) ->
|
||||
gen_fsm:start(ServerRef, ?MODULE, [Module, Args], Options).
|
||||
|
||||
%% @hidden
|
||||
start_link(Module, Args, Options) ->
|
||||
gen_fsm:start_link(?MODULE, [Module, Args], Options).
|
||||
|
||||
%% @hidden
|
||||
start_link(ServerRef, Module, Args, Options) ->
|
||||
gen_fsm:start_link(ServerRef, ?MODULE, [Module, Args], Options).
|
||||
|
||||
%% @hidden
|
||||
call(ServerRef, Request) ->
|
||||
gen_server:call(ServerRef, Request).
|
||||
|
||||
%% @hidden
|
||||
call(ServerRef, Request, Timeout) ->
|
||||
gen_server:call(ServerRef, Request, Timeout).
|
||||
|
||||
%% @hidden
|
||||
multi_call(Name, Request) ->
|
||||
gen_server:multi_call(Name, Request).
|
||||
|
||||
%% @hidden
|
||||
multi_call(Nodes, Name, Request) ->
|
||||
gen_server:multi_call(Nodes, Name, Request).
|
||||
|
||||
%% @hidden
|
||||
multi_call(Nodes, Name, Request, Timeout) ->
|
||||
gen_server:multi_call(Nodes, Name, Request, Timeout).
|
||||
|
||||
%% @hidden
|
||||
cast(ServerRef, Request) ->
|
||||
gen_server:cast(ServerRef, Request).
|
||||
|
||||
%% @hidden
|
||||
abcast(Name, Request) ->
|
||||
gen_server:abcast(Name, Request).
|
||||
|
||||
%% @hidden
|
||||
abcast(Nodes, Name, Request) ->
|
||||
gen_server:abcast(Nodes, Name, Request).
|
||||
|
||||
%% @hidden
|
||||
reply(Client, Reply) ->
|
||||
gen_server:reply(Client, Reply).
|
||||
|
||||
%% @hidden
|
||||
enter_loop(Module, Options, State, ServerName, Timeout) ->
|
||||
gen_server:enter_loop(Module, Options, State, ServerName, Timeout).
|
||||
|
||||
%% @hidden
|
||||
enter_loop(Module, Options, State, Timeout) ->
|
||||
gen_server:enter_loop(Module, Options, State, Timeout).
|
||||
% enter_loop(Module, Options, State, ServerName) ->
|
||||
% gen_server:enter_loop(Module, Options, State, ServerName).
|
||||
|
|
@ -0,0 +1,405 @@
|
|||
%%% $Id: tcap_tsm_fsm.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Transaction State Machine (TCM) functional block within the
|
||||
%%% transaction sub-layer of ITU TCAP.
|
||||
%%%
|
||||
%%% @reference ITU-T Q.774 (06/97) Annex A Transaction capabilities SDLs
|
||||
%%%
|
||||
%%% @private
|
||||
%%%
|
||||
|
||||
-module(tcap_tsm_fsm).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
-behaviour(gen_fsm).
|
||||
|
||||
%% call backs needed for gen_fsm behaviour
|
||||
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
|
||||
terminate/3, code_change/4]).
|
||||
|
||||
%% transaction_fsm state callbacks
|
||||
-export([idle/2, initiation_sent/2, initiation_received/2, active/2]).
|
||||
|
||||
%% record definitions for TR-User primitives
|
||||
-include("tcap.hrl").
|
||||
%% record definitions for N-User primitives
|
||||
-include("sccp.hrl").
|
||||
%% record definitions for TCAP messages
|
||||
-include("TCAPMessages.hrl").
|
||||
|
||||
%% the transaction_fsm state data
|
||||
-record(state, {nsap, usap, tco, supervisor, supref, localTID, remoteTID,
|
||||
local_address, remote_address, dha}).
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The gen_fsm call backs
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% initialize the server
|
||||
init({NSAP, USAP, TID, Supervisor, SupRef, TCO}) ->
|
||||
%% store our process identifier in the global transaction ID table
|
||||
ets:insert(transaction, {TID, self()}),
|
||||
process_flag(trap_exit, true),
|
||||
{ok, idle, #state{nsap = NSAP, usap = USAP, localTID = TID, supervisor = Supervisor,
|
||||
supref = SupRef, tco = TCO}}.
|
||||
|
||||
%%%
|
||||
%%% idle state handler
|
||||
%%%
|
||||
|
||||
%% started by remote
|
||||
%% reference: Figure A.4/Q.774 (sheet 1 of 5)
|
||||
idle({'BEGIN', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
%% Store remote address and remote TID
|
||||
NewState = State#state{remote_address = SccpParms#'N-UNITDATA'.callingAddress,
|
||||
remoteTID = (SccpParms#'N-UNITDATA'.userData)#'Begin'.otid},
|
||||
{ok, Begin} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
%% Start a Dialogue Handler (DHA)
|
||||
SupId = list_to_atom("dha_sup_" ++ integer_to_list(State#state.localTID)),
|
||||
StartFunc = {supervisor, start_link,
|
||||
[dialogue_sup, [{State#state.usap, State#state.localTID, self()}]]},
|
||||
ChildSpec = {SupId, StartFunc, permanent, infinity, supervisor, [dialogue_sup]},
|
||||
{ok, DHA} = supervisor:start_child(State#state.supervisor, ChildSpec),
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl, SccpParms#'N-UNITDATA'.returnOption},
|
||||
UserData = #'TR-user-data'{dialoguePortion = Begin#'Begin'.dialoguePortion,
|
||||
componentPortion = Begin#'Begin'.components},
|
||||
TrParms = #'TR-BEGIN'{qos = QOS,
|
||||
destAddress = SccpParms#'N-UNITDATA'.calledAddress,
|
||||
origAddress = SccpParms#'N-UNITDATA'.callingAddress,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
%% TR-BEGIN CSL <- TSL
|
||||
gen_fsm:send_event(DHA, {'TR', 'BEGIN', indication, TrParms}),
|
||||
{next_state, initiation_received, NewState#state{dha = DHA}};
|
||||
|
||||
%% started by TR-User
|
||||
%% reference: Figure A.4/Q.774 (sheet 1 of 5)
|
||||
idle({'BEGIN', transaction, BeginParms}, State)
|
||||
when is_record(BeginParms, 'TR-BEGIN') ->
|
||||
%% Store local address
|
||||
%% NOTE - This may be provided by TC-user or be implicitly associated with
|
||||
%% the access point at which the N-UNITDATA primitive is issued.
|
||||
NewState = State#state{local_address = BeginParms#'TR-BEGIN'.origAddress},
|
||||
DialoguePortion = (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (BeginParms#'TR-BEGIN'.userData)#'TR-user-data'.componentPortion,
|
||||
Begin = #'Begin'{otid = State#state.localTID, dialoguePortion = DialoguePortion,
|
||||
components = ComponentPortion},
|
||||
%% Assemble TR-portion of BEGIN message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {'begin', Begin})),
|
||||
{SequenceControl, ReturnOption} = BeginParms#'TR-BEGIN'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = BeginParms#'TR-BEGIN'.destAddress,
|
||||
callingAddress = BeginParms#'TR-BEGIN'.origAddress,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(NewState#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{next_state, initiation_sent, NewState}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% initiation_received state handler
|
||||
%%%
|
||||
%%% reference: Figure A.4/Q.774 (sheet 2 of 5)
|
||||
|
||||
%% Continue from TR-User
|
||||
initiation_received({'CONTINUE', transaction, ContParms}, State)
|
||||
when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
%% Store new local address if it is provided by User
|
||||
case ContParms#'TR-CONTINUE'.origAddress of
|
||||
undefined ->
|
||||
NewState = State;
|
||||
NewAddress ->
|
||||
NewState = State#state{local_address = NewAddress}
|
||||
end,
|
||||
DialoguePortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.componentPortion,
|
||||
Continue = #'Continue'{otid = State#state.localTID, dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of CONTINUE message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {continue, Continue})),
|
||||
{SequenceControl, ReturnOption} = ContParms#'TR-CONTINUE'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = NewState#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(NewState#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{next_state, active, NewState};
|
||||
|
||||
%% End from TR-User (prearranged)
|
||||
initiation_received({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END'),
|
||||
EndParms#'TR-END'.termination == prearranged ->
|
||||
{stop, normal, State};
|
||||
%% End from TR-User (not prearranged)
|
||||
initiation_received({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
DialoguePortion = (EndParms#'TR-END'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion,
|
||||
End = #'End'{dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of END message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {'end', End})),
|
||||
{SequenceControl, ReturnOption} = EndParms#'TR-END'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from TR-User
|
||||
initiation_received({'ABORT', transaction, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
Cause = (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion,
|
||||
Abort = #'Abort'{reason = {'u-abortCause', Cause}},
|
||||
%% Assemble TR-portion of ABORT message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {abort, Abort})),
|
||||
{SequenceControl, ReturnOption} = AbortParms#'TR-U-ABORT'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% initiation_sent state handler
|
||||
%%%
|
||||
%%% reference: Figure A.4/Q.774 (sheet 2 of 5)
|
||||
|
||||
%% Continue from remote
|
||||
initiation_sent({'CONTINUE', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
%% Store remote address and remote TID
|
||||
OTID = (SccpParms#'N-UNITDATA'.userData)#'Begin'.otid,
|
||||
NewState = State#state{ remote_address
|
||||
= SccpParms#'N-UNITDATA'.callingAddress, remoteTID = OTID},
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Continue} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = Continue#'Continue'.dialoguePortion,
|
||||
componentPortion = Continue #'Continue'.components},
|
||||
TrParms = #'TR-CONTINUE'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(NewState#state.dha, {'TR', 'CONTINUE', indication, TrParms}),
|
||||
{next_state, active, NewState};
|
||||
|
||||
%% End from remote
|
||||
initiation_sent({'END', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, End} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = End#'End'.dialoguePortion,
|
||||
componentPortion = End#'End'.components},
|
||||
TrParms = #'TR-END'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'END', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from remote
|
||||
initiation_sent({'ABORT', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Abort} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
%% TR-U-ABORT?
|
||||
case Abort#'Abort'.reason of
|
||||
{'p-abortCause', Cause} ->
|
||||
TrParms = #'TR-P-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
pAbort = Cause},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms});
|
||||
{'u-abortCause', Cause} ->
|
||||
UserData = #'TR-user-data'{dialoguePortion = Cause},
|
||||
TrParms = #'TR-U-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'U-ABORT', indication, TrParms})
|
||||
end,
|
||||
{stop, normal, State};
|
||||
|
||||
%% Local Abort
|
||||
initiation_sent({'local-abort', received, Cause}, State) ->
|
||||
TrParms = #'TR-P-ABORT'{pAbort = Cause},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% End from TR-User
|
||||
initiation_sent({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from TR-User
|
||||
initiation_sent({'ABORT', transaction, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
%% Purely local action
|
||||
{stop, normal, State}.
|
||||
|
||||
|
||||
%%%
|
||||
%%% active state handler
|
||||
%%%
|
||||
%%% reference: Figure A.4/Q.774 (sheet 2 of 5)
|
||||
|
||||
%% Continue received from remote
|
||||
active({'CONTINUE', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Continue} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = Continue#'Continue'.dialoguePortion,
|
||||
componentPortion = Continue#'Continue'.components},
|
||||
TrParms = #'TR-CONTINUE'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'CONTINUE', indication, TrParms}),
|
||||
{next_state, active, State};
|
||||
|
||||
%% Continue from TR-User
|
||||
active({'CONTINUE', transaction, ContParms}, State)
|
||||
when is_record(ContParms, 'TR-CONTINUE') ->
|
||||
DialoguePortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (ContParms#'TR-CONTINUE'.userData)#'TR-user-data'.componentPortion,
|
||||
Continue = #'Continue'{dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of CONTINUE message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {continue, Continue})),
|
||||
{SequenceControl, ReturnOption} = ContParms#'TR-CONTINUE'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{next_state, active, State};
|
||||
|
||||
%% End from remote
|
||||
active({'END', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, End} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
UserData = #'TR-user-data'{dialoguePortion = End#'End'.dialoguePortion,
|
||||
componentPortion = End#'End'.components},
|
||||
TrParms = #'TR-END'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
%% TR-END indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'END', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% End from TR-User (prearranged)
|
||||
active({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END'),
|
||||
EndParms#'TR-END'.termination == prearranged ->
|
||||
{stop, normal, State};
|
||||
%% End from TR-User (not prearranged)
|
||||
active({'END', transaction, EndParms}, State)
|
||||
when is_record(EndParms, 'TR-END') ->
|
||||
DialoguePortion = (EndParms#'TR-END'.userData)#'TR-user-data'.dialoguePortion,
|
||||
ComponentPortion = (EndParms#'TR-END'.userData)#'TR-user-data'.componentPortion,
|
||||
End = #'End'{dialoguePortion = DialoguePortion, components = ComponentPortion},
|
||||
%% Assemble TR-portion of END message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {'end', End})),
|
||||
{SequenceControl, ReturnOption} = EndParms#'TR-END'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort received from remote
|
||||
active({'ABORT', received, SccpParms}, State)
|
||||
when is_record(SccpParms, 'N-UNITDATA') ->
|
||||
QOS = {SccpParms#'N-UNITDATA'.sequenceControl,
|
||||
SccpParms#'N-UNITDATA'.returnOption},
|
||||
{ok, Abort} = 'TR':decode('TCMessage', SccpParms#'N-UNITDATA'.userData),
|
||||
%% TR-U-ABORT?
|
||||
case Abort#'Abort'.reason of
|
||||
{'p-abortCause', Cause} -> % No
|
||||
TrParms = #'TR-P-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
pAbort = Cause},
|
||||
%% TR-P-ABORT indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms});
|
||||
{'u-abortCause', Cause} -> % Yes
|
||||
UserData = #'TR-user-data'{dialoguePortion = Cause},
|
||||
TrParms = #'TR-U-ABORT'{qos = QOS,
|
||||
transactionID = State#state.localTID,
|
||||
userData = UserData},
|
||||
%% TR-U-ABORT indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'U-ABORT', indication, TrParms})
|
||||
end,
|
||||
{stop, normal, State};
|
||||
|
||||
%% Local Abort
|
||||
active({'local-abort', received, Cause}, State) ->
|
||||
TrParms = #'TR-P-ABORT'{qos = {false, false},
|
||||
transactionID = State#state.localTID, pAbort= Cause},
|
||||
%% TR-P-ABORT indication CSL <- TSL
|
||||
gen_fsm:send_event(State#state.dha, {'TR', 'P-ABORT', indication, TrParms}),
|
||||
{stop, normal, State};
|
||||
|
||||
%% Abort from TR-User
|
||||
active({'ABORT', transaction, AbortParms}, State)
|
||||
when is_record(AbortParms, 'TR-U-ABORT') ->
|
||||
Cause = (AbortParms#'TR-U-ABORT'.userData)#'TR-user-data'.dialoguePortion,
|
||||
Abort = #'Abort'{reason = {'u-abortCause', Cause}},
|
||||
%% Assemble TR-portion of ABORT message
|
||||
TPDU = list_to_binary('TR':encode('TCMessage', {abort, Abort})),
|
||||
{SequenceControl, ReturnOption} = AbortParms#'TR-U-ABORT'.qos,
|
||||
SccpParms = #'N-UNITDATA'{calledAddress = State#state.remote_address,
|
||||
callingAddress = State#state.local_address,
|
||||
sequenceControl = SequenceControl, returnOption = ReturnOption,
|
||||
importance = none, userData = TPDU},
|
||||
%% N-UNITDATA request TSL -> SCCP
|
||||
gen_fsm:send_event(State#state.nsap, {'N', 'UNITDATA', request, SccpParms}),
|
||||
{stop, normal, State}.
|
||||
|
||||
|
||||
%% handle an event sent using gen_fsm:send_all_state_event/2
|
||||
handle_event(Event, StateName, State) ->
|
||||
error_logger:format("transaction_fsm (~w) received unexpected message: ~w~n", [Event]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle an event sent using gen_fsm:sync_send_all_state_event/2,3
|
||||
handle_sync_event(Event, _From, StateName, State) ->
|
||||
error_logger:format("transaction_fsm (~w) received unexpected message: ~w~n", [Event]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle any other message
|
||||
handle_info(Info, StateName, State) ->
|
||||
error_logger:format("transaction_fsm (~w) received unexpected message: ~w~n", [Info]),
|
||||
{next_state, StateName, State}.
|
||||
|
||||
%% handle a shutdown request
|
||||
terminate(_Reason, _StateName, State) ->
|
||||
ets:delete(transaction, State#state.localTID),
|
||||
%% signal TCO that we are stopping
|
||||
gen_server:cast(State#state.supervisor, {'tsm-stopped', State#state.supref}).
|
||||
|
||||
%% handle updating state data due to a code replacement
|
||||
code_change(_OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
## $Id: Makefile,v 1.5 2005/02/10 05:20:17 vances Exp $
|
||||
|
||||
EBIN = ../ebin
|
||||
|
||||
ERL = erl
|
||||
ERLC = erlc
|
||||
EMULATOR = beam
|
||||
ERLCFLAGS = -b $(EMULATOR) -o $(EBIN) -W -v +warn_unused_vars
|
||||
|
||||
$(EBIN)/%.$(EMULATOR):%.erl
|
||||
$(ERLC) $(ERLCFLAGS) $<
|
||||
|
||||
BEAMS = $(EBIN)/tcap.$(EMULATOR) $(EBIN)/tcap_app.$(EMULATOR) \
|
||||
$(EBIN)/tcap_sup.$(EMULATOR) \
|
||||
$(EBIN)/tcap_sap_sup.$(EMULATOR) \
|
||||
$(EBIN)/tcap_transaction_sup.$(EMULATOR) \
|
||||
$(EBIN)/tcap_dialogue_sup.$(EMULATOR) \
|
||||
$(EBIN)/tcap_components_sup.$(EMULATOR) \
|
||||
$(EBIN)/tcap_invocation_sup.$(EMULATOR)
|
||||
|
||||
.PHONY: default
|
||||
default: all
|
||||
|
||||
.PHONY: all
|
||||
all: $(BEAMS) itu ansi
|
||||
|
||||
.PHONY: itu
|
||||
itu:
|
||||
cd itu && $(MAKE)
|
||||
|
||||
.PHONY: ansi
|
||||
ansi:
|
||||
cd ansi && $(MAKE)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
- rm -f $(BEAMS)
|
||||
cd itu && $(MAKE) $@
|
||||
cd ansi && $(MAKE) $@
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
%%% $Id: tcap.erl,v 1.10 2011/03/07 17:21:09 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc Transaction Capabilities Application Part
|
||||
%%% <p>This module implements the user's API to the TCAP protocol
|
||||
%%% stack application.Transaction Capabilities are </p>
|
||||
%%%
|
||||
%%% @reference <a href="index.html">TCAP User's Guide</a>
|
||||
%%%
|
||||
|
||||
-module(tcap).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.10 $').
|
||||
|
||||
%% our published API functions
|
||||
-export([start/0, stop/0]).
|
||||
-export([open/3, close/1]).
|
||||
%-export([start_tsl/3]).
|
||||
-export([dialogueID/1, transactionID/1]).
|
||||
|
||||
|
||||
%% @type tcap_options(). TCAP layer options
|
||||
%% <p>A list of one or more of the following tuples.</p>
|
||||
%% <dl>
|
||||
%% <dt><tt>{variant, Variant}</tt></dt><dd><tt>itu | ansi</tt></dd>
|
||||
%% </dl>
|
||||
%% @end
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% The API functions
|
||||
%%----------------------------------------------------------------------
|
||||
|
||||
%% @spec () -> Result
|
||||
%% Result = ok | {error, Reason}
|
||||
%% Reason = term()
|
||||
%%
|
||||
%% @equiv application:start(tcap)
|
||||
%%
|
||||
start() ->
|
||||
application:start(tcap).
|
||||
|
||||
%% @spec () -> Result
|
||||
%% Result = ok | {error, Reason}
|
||||
%% Reason = term()
|
||||
%%
|
||||
%% @equiv application:stop(tcap)
|
||||
%%
|
||||
stop() ->
|
||||
application:stop(tcap).
|
||||
|
||||
%% @spec (TSL::pid(), TCU::pid(), Args) -> CSL
|
||||
%% Args = [term()]
|
||||
%% CSL = {DHA, CCO}
|
||||
%% DHA = pid()
|
||||
%% CCO = pid()
|
||||
%%
|
||||
%% @doc Start a new component sublayer (CSL).
|
||||
%% <p>Called by the TC-User to initialize the TCAP layer for a new
|
||||
%% dialogue.</p>
|
||||
%%
|
||||
%% <p><tt>TSL</tt> is the pid returned from a previous call to
|
||||
%% <a href="#open-3"><tt>open/3</tt></a>.</p>
|
||||
%%
|
||||
%% <p><tt>TCU</tt> is the pid of the TC-User.</p>
|
||||
%%
|
||||
%% <p>Returns <tt>{DHA, CCO}</tt>; the pids of the dialogue handler
|
||||
%% and component coordinator in the component sublayer.</p>.
|
||||
%%
|
||||
open(TSL, TCU, Args) ->
|
||||
gen_server:call(TSL, {start_dialogue, TCU, Args}).
|
||||
|
||||
%% @spec (TSL::pid()) -> ok
|
||||
%%
|
||||
%% @doc Close a TCAP service layer.
|
||||
%%
|
||||
%% <p><tt>TSL</tt> is the pid returned in a previous call to
|
||||
%% <a href="#open-3"><tt>open/3</tt></a>.</p>
|
||||
%%
|
||||
close(TSL) when is_pid(TSL) ->
|
||||
gen_server:call(TSL, close).
|
||||
|
||||
%% @spec (TSL::pid()) -> tid()
|
||||
%%
|
||||
%% @doc Assign a new dialogue ID.
|
||||
%%
|
||||
%% <p><tt>TSL</tt> is the pid returned in a previous call to
|
||||
%% <a href="#open-3"><tt>open/3</tt></a>.</p>
|
||||
%%
|
||||
dialogueID(TSL) when is_pid(TSL) ->
|
||||
gen_server:call(TSL, dialogueID).
|
||||
|
||||
%% @equiv dialogueID/0
|
||||
%%
|
||||
transactionID(TSL) when is_pid(TSL) ->
|
||||
dialogueID(TSL).
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
%%% $Id: tcap_app.erl,v 1.3 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2004-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc TCAP application callback module.
|
||||
%%%
|
||||
%%% @reference <a href="index.html">TCAP User's Guide</a>
|
||||
%%%
|
||||
%%% @private
|
||||
|
||||
-module(tcap_app).
|
||||
-copyright('Copyright (c) 2004-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.3 $').
|
||||
|
||||
%% export application behaviour callbacks
|
||||
-export([start/2, start_phase/3, prep_stop/1, stop/1, config_change/3]).
|
||||
|
||||
|
||||
%% @spec(StartType, StartArgs::term()) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}
|
||||
%% StartType = normal | {takeover,Node} | {failover,Node}
|
||||
%% Node = node()
|
||||
%% Pid = pid()
|
||||
%% State = term()
|
||||
%% Reason = term()
|
||||
%%
|
||||
%% @equiv //kernel/application:start/3
|
||||
%%
|
||||
start(normal, StartArgs) ->
|
||||
ets:new(transaction, [named_table, public]),
|
||||
{ok, SupRef} = application:get_env(supref),
|
||||
supervisor:start_link(SupRef, tcap_sup, StartArgs).
|
||||
|
||||
%% @spec(Phase::atom(), StartType, PhaseArgs::term()) -> ok | {error, Reason}
|
||||
%% StartType = normal | {takeover,Node} | {failover,Node}
|
||||
%% Node = node()
|
||||
%% Reason = term()
|
||||
%%
|
||||
%% @equiv //kernel/application:start_phase/3
|
||||
%%
|
||||
start_phase(_Phase, _StartType, _PhaseArgs) ->
|
||||
ok.
|
||||
|
||||
%% @spec(State::term()) -> NewState
|
||||
%% NewState = term()
|
||||
%%
|
||||
%% @equiv //kernel/application:prep_stop/1
|
||||
%%
|
||||
prep_stop(State) ->
|
||||
State.
|
||||
|
||||
%% @spec(State) -> ok
|
||||
%%
|
||||
%% @equiv //kernel/application:stop/1
|
||||
%%
|
||||
stop(_State) ->
|
||||
ok.
|
||||
|
||||
%% @spec(Changed, New, Removed) ->
|
||||
%% Changed = [{Par,Val}]
|
||||
%% New = [{Par,Val}]
|
||||
%% Removed = [Par]
|
||||
%% Par = atom()
|
||||
%% Val = term()
|
||||
%%
|
||||
%% @equiv //kernel/application:config_change/3
|
||||
%%
|
||||
config_change(_Changed, _New, _Removed) ->
|
||||
ok.
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
%%% $Id: tcap_components_sup.erl,v 1.2 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
|
||||
-module(tcap_components_sup).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.2 $').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% call backs needed for supervisor behaviour
|
||||
-export([init/1]).
|
||||
|
||||
init([USAP, ID]) ->
|
||||
Name = list_to_atom("cco_" ++ integer_to_list(ID)),
|
||||
StartArgs = [tcap_cco_server, [USAP, ID, self()], []],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
ChildSpec = {Name, StartFunc, temporary, 4000, worker,
|
||||
[tcap_cco_server]},
|
||||
{ok,{{one_for_all, 0, 1}, [ChildSpec]}}.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
%%% $Id: tcap_dialogue_sup.erl,v 1.2 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
|
||||
-module(tcap_dialogue_sup).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.2 $').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% call backs needed for supervisor behaviour
|
||||
-export([init/1]).
|
||||
|
||||
%% when started from TCO
|
||||
init({USAP, LocalTID, TCO, SupId}) ->
|
||||
StartArgs = [tcap_dha_fsm, [{USAP, LocalTID, TCO, SupId, self()}], []],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
ChildSpec = {dha, StartFunc, permanent, 4000, worker,
|
||||
[tcap_dha_fsm]},
|
||||
{ok,{{one_for_all, 0, 1}, [ChildSpec]}};
|
||||
|
||||
%% when started from TSM
|
||||
init({USAP, LocalTID, TCO}) ->
|
||||
StartArgs = [tcap_dha_fsm, [{USAP, LocalTID, TCO, self()}], []],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
ChildSpec = {dha, StartFunc, permanent, 4000, worker,
|
||||
[tcap_dha_fsm]},
|
||||
{ok,{{one_for_all, 0, 1}, [ChildSpec]}}.
|
|
@ -0,0 +1,33 @@
|
|||
%%% $Id: tcap_invocation_sup.erl,v 1.2 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
|
||||
-module(tcap_invocation_sup).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.2 $').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% call backs needed for supervisor behaviour
|
||||
-export([init/1]).
|
||||
|
||||
init([USAP, InvokeID]) ->
|
||||
Name = list_to_atom("ism_" ++ integer_to_list(InvokeID)),
|
||||
StartArgs = [tcap_ism_fsm, [USAP, InvokeID], []],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
ChildSpec = {Name, StartFunc, temporary, 4000, worker,
|
||||
[tcap_ism_sup]},
|
||||
{ok,{{one_for_all, 0, 1}, [ChildSpec]}}.
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
%%% $Id: tcap_sap_sup.erl,v 1.5 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc TCAP service access point supervisor.
|
||||
%%%
|
||||
%%% @reference <a href="index.html">TCAP User's Guide</a>
|
||||
%%%
|
||||
%%% @private
|
||||
|
||||
-module(tcap_sap_sup).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.5 $').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
|
||||
%% @spec(StartArgs::term()) -> Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore
|
||||
%% RestartStrategy = one_for_all | one_for_one | rest_for_one | simple_one_for_one
|
||||
%% MaxR = MaxT = int()>=0
|
||||
%% ChildSpec = child_spec()
|
||||
%%
|
||||
%% @see supervisor:init/1
|
||||
%%
|
||||
init([Module, Args, Options]) when is_list(Args), is_list(Options) ->
|
||||
% Name = list_to_atom("tcap_ssn" ++ integer_to_list(SSN)),
|
||||
StartArgs = [Module, [self()] ++ Args, Options],
|
||||
StartFunc = {gen_server, start_link, StartArgs},
|
||||
ChildSpec = {tco, StartFunc, permanent, 4000, worker, [Module, tcap_tco_server]},
|
||||
{ok,{{one_for_one, 10, 60}, [ChildSpec]}}.
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
%%% $Id: tcap_sup.erl,v 1.2 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @doc TCAP application top level supervisor.
|
||||
%%%
|
||||
%%% @reference <a href="index.html">TCAP User's Guide</a>
|
||||
%%%
|
||||
%%% @private
|
||||
|
||||
|
||||
-module(tcap_sup).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.2 $').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% call backs needed for supervisor behaviour
|
||||
-export([init/1]).
|
||||
|
||||
%% @spec(StartArgs::term()) -> Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore
|
||||
%% RestartStrategy = one_for_all | one_for_one | rest_for_one | simple_one_for_one
|
||||
%% MaxR = MaxT = int()>=0
|
||||
%% ChildSpec = child_spec()
|
||||
%%
|
||||
%% @equiv //stdlib/supervisor:init/1
|
||||
%%
|
||||
init(_StartArgs) ->
|
||||
StartMod = tcap_sap_sup,
|
||||
StartFunc = {supervisor, start_link, [StartMod]},
|
||||
ChildSpec = {sap_sup, StartFunc, permanent, infinity, supervisor, [StartMod]},
|
||||
{ok,{{simple_one_for_one, 10, 60}, ChildSpec}}.
|
|
@ -0,0 +1,32 @@
|
|||
%%% $Id: tcap_transaction_sup.erl,v 1.2 2005/08/04 09:33:17 vances Exp $
|
||||
%%%---------------------------------------------------------------------
|
||||
%%% @copyright 2004-2005 Motivity Telecom Inc.
|
||||
%%% @author Vance Shipley <vances@motivity.ca> [http://www.motivity.ca]
|
||||
%%% @end
|
||||
%%%
|
||||
%%% Copyright Motivity Telecom Inc. 2003-2005
|
||||
%%%
|
||||
%%% All rights reserved. No part of this computer program(s) may be
|
||||
%%% used, reproduced, stored in any retrieval system, or transmitted,
|
||||
%%% in any form or by any means, electronic, mechanical, photocopying,
|
||||
%%% recording, or otherwise without prior written permission of
|
||||
%%% Motivity Telecom Inc.
|
||||
%%%---------------------------------------------------------------------
|
||||
|
||||
-module(tcap_transaction_sup).
|
||||
-copyright('Copyright (c) 2003-2005 Motivity Telecom Inc.').
|
||||
-author('vances@motivity.ca').
|
||||
-vsn('$Revision: 1.2 $').
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% call backs needed for supervisor behaviour
|
||||
-export([init/1]).
|
||||
|
||||
init({NSAP, USAP, TID, SupRef}) ->
|
||||
Name = list_to_atom("tsm_" ++ integer_to_list(TID)),
|
||||
StartArgs = [tcap_tsm_fsm, [NSAP, USAP, TID, self(), SupRef], []],
|
||||
StartFunc = {gen_fsm, start_link, StartArgs},
|
||||
ChildSpec = {Name, StartFunc, permanent, 1000, worker, [tcap_tsm_fsm]},
|
||||
{ok,{{one_for_all, 0, 1}, [ChildSpec]}}.
|
||||
|