aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2013-06-17 15:15:35 +0200
committerHolger Hans Peter Freyther <zecke@selfish.org>2013-06-17 15:15:35 +0200
commit6f7e0bcf8d0483770fa6e13b66953f9917cf84f3 (patch)
tree09351a9b3dd2860792e67405c619a374c6dd5efa
parent372c2e0f0b6ac35098bedc31aace9c5b7d36fee0 (diff)
m2ua: Merge the ASP changes from Pharo to GST
-rw-r--r--Makefile6
-rw-r--r--core/ExtensionsGST.st28
-rw-r--r--m2ua/M2UAApplicationServerProcess.st507
-rw-r--r--m2ua/M2UAAspStateMachine.st106
-rw-r--r--m2ua/M2UAExamples.st42
-rw-r--r--m2ua/M2UALayerManagement.st127
-rw-r--r--m2ua/M2UAMSG.st148
-rw-r--r--m2ua/M2UAMessages.st217
-rw-r--r--m2ua/M2UAStates.st42
-rw-r--r--m2ua/M2UATerminology.st44
-rw-r--r--m2ua/M2UATests.st222
-rw-r--r--package.xml18
12 files changed, 1474 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index 0a8cdd6..986159d 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,11 @@ UA = \
ua/XUA.st
M2UA = \
- m2ua/M2UAConstants.st m2ua/M2UAMSG.st m2ua/M2UATag.st m2ua/M2UAStates.st
+ m2ua/M2UAConstants.st m2ua/M2UAMSG.st m2ua/M2UATag.st m2ua/M2UAMessages.st \
+ m2ua/M2UAStates.st m2ua/M2UAAspStateMachine.st \
+ m2ua/M2UAApplicationServerProcess.st m2ua/M2UALayerManagement.st \
+ m2ua/M2UAExamples.st m2ua/M2UATerminology.st m2ua/M2UATests.st
+
OSMO = \
osmo/LogAreaOsmo.st \
diff --git a/core/ExtensionsGST.st b/core/ExtensionsGST.st
new file mode 100644
index 0000000..efeec62
--- /dev/null
+++ b/core/ExtensionsGST.st
@@ -0,0 +1,28 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+BlockClosure extend [
+ value: arg1 value: arg2 value: arg3 value: arg4 [
+ <category: '*OsmoNetwork'>
+ "Evaluate the receiver passing arg1, arg2, arg3 and arg4 as the parameters"
+
+ <category: 'built ins'>
+ <primitive: VMpr_BlockClosure_value>
+ SystemExceptions.WrongArgumentCount signal
+ ]
+]
diff --git a/m2ua/M2UAApplicationServerProcess.st b/m2ua/M2UAApplicationServerProcess.st
new file mode 100644
index 0000000..e9fb1aa
--- /dev/null
+++ b/m2ua/M2UAApplicationServerProcess.st
@@ -0,0 +1,507 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+Object subclass: M2UAApplicationServerProcess [
+ | socket asp_active_block asp_down_block asp_inactive_block asp_up_block error_block notify_block sctp_confirm_block sctp_released_block sctp_restarted_block sctp_status_block established state t_ack lastMsg on_state_change as_state |
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: 'I am a M2UA Application Server Process.
+
+I have an internal state machine and a state and will be used by the
+M2UA Layer. I am written for the usage in a Media Gateway Controller
+and will also keep information about the Application Server itself.
+
+If I need to be used on a Signalling Gateway (SG) I will need a dedicated
+M2UA Application Server class and state machine.
+
+I can currently only manage a single interface. The specification allows
+a single ASP to send one ASPActive for one interface at a time.'>
+
+ M2UAApplicationServerProcess class >> initWith: aService [
+ ^self new
+ socketService: aService;
+ yourself
+ ]
+
+ M2UAApplicationServerProcess class >> new [
+ ^super new
+ initialize;
+ yourself
+ ]
+
+ onError: aBlock [
+ "M-ERROR indication
+ Direction: M2UA -> LM
+ Purpose: ASP or SGP reports that it has received an ERROR
+ message from its peer."
+
+ <category: 'Primitives-LayerManagement'>
+ error_block := aBlock
+ ]
+
+ onNotify: aBlock [
+ "M-NOTIFY indication
+ Direction: M2UA -> LM
+ Purpose: ASP reports that it has received a NOTIFY message
+ from its peer."
+
+ <category: 'Primitives-LayerManagement'>
+ notify_block := aBlock
+ ]
+
+ onSctpEstablished: aBlock [
+ "M-SCTP_ESTABLISH confirm
+ Direction: M2UA -> LM
+ Purpose: ASP confirms to LM that it has established an SCTP association with an SGP."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ sctp_confirm_block := aBlock
+ ]
+
+ onSctpReleased: aBlock [
+ "M-SCTP_RELEASE confirm
+ Direction: M2UA -> LM
+ Purpose: ASP confirms to LM that it has released SCTP association with SGP."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ sctp_released_block := aBlock
+ ]
+
+ onSctpRestarted: aBlock [
+ "M-SCTP_RELEASE indication
+ Direction: M2UA -> LM
+ Purpose: SGP informs LM that ASP has released an SCTP association."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ sctp_restarted_block := aBlock
+ ]
+
+ onSctpStatus: aBlock [
+ "M-SCTP_STATUS indication
+ Direction: M2UA -> LM
+ Purpose: M2UA reports status of SCTP association."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ sctp_status_block := aBlock
+ ]
+
+ sctpEstablish [
+ "M-SCTP_ESTABLISH request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to establish an SCTP association with an SGP."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ established := false.
+ socket stop.
+ socket start
+ ]
+
+ sctpRelease [
+ "M-SCTP_RELEASE request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to release an SCTP association with SGP."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ established := false.
+ socket stop.
+ t_ack ifNotNil: [t_ack cancel]
+ ]
+
+ sctpStatusRequest [
+ "M-SCTP_STATUS request
+ Direction: LM -> M2UA
+ Purpose: LM requests M2UA to report status of SCTP association."
+
+ <category: 'Primitives-LayerManagement-SCTP'>
+ self notYetImplemented
+ ]
+
+ aspActive [
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ "M-ASP_ACTIVE request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to send an ASP ACTIVE message to the SGP."
+
+ | msg |
+ self checkNextState: M2UAAspStateActive.
+ msg := M2UAMSG new
+ class: M2UAConstants clsASPTM;
+ msgType: M2UAConstants asptmActiv;
+ addTag: self createIdentIntTag;
+ addTag: self createInfoTag;
+ yourself.
+ self send: msg
+ ]
+
+ aspDown [
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ "M-ASP_DOWN request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to stop its operation and send an ASP DOWN
+ message to the SGP."
+
+ | msg |
+ self checkNextState: M2UAAspStateDown.
+ msg := M2UAMSG new
+ class: M2UAConstants clsASPSM;
+ msgType: M2UAConstants aspsmDown;
+ addTag: self createAspIdentTag;
+ addTag: self createInfoTag;
+ yourself.
+ self send: msg
+ ]
+
+ aspInactive [
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ "M-ASP_INACTIVE request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to send an ASP INACTIVE message to the SGP."
+
+ | msg |
+ self checkNextState: M2UAAspStateInactive.
+ msg := M2UAMSG new
+ class: M2UAConstants clsASPTM;
+ msgType: M2UAConstants asptmInactiv;
+ addTag: self createIdentIntTag;
+ addTag: self createInfoTag;
+ yourself.
+ self send: msg
+ ]
+
+ aspUp [
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ "M-ASP_UP request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to start its operation and send an ASP UP
+ message to the SGP."
+
+ | msg |
+ self checkNextState: M2UAAspStateInactive.
+ msg := M2UAMSG new
+ class: M2UAConstants clsASPSM;
+ msgType: M2UAConstants aspsmUp;
+ addTag: self createAspIdentTag;
+ addTag: self createInfoTag;
+ yourself.
+ self send: msg
+ ]
+
+ onAspActive: aBlock [
+ "M-ASP_ACTIVE confirm
+ Direction: M2UA -> LM
+ Purpose: ASP reports that is has received an ASP ACTIVE
+ Acknowledgment message from the SGP."
+
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ asp_active_block := aBlock
+ ]
+
+ onAspDown: aBlock [
+ "M-ASP_DOWN confirm
+ Direction: M2UA -> LM
+ Purpose: ASP reports that is has received an ASP DOWN Acknowledgment
+ message from the SGP."
+
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ asp_down_block := aBlock
+ ]
+
+ onAspInactive: aBlock [
+ "M-ASP_INACTIVE confirm
+ Direction: M2UA -> LM
+ Purpose: ASP reports that is has received an ASP INACTIVE
+ Acknowledgment message from the SGP."
+
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ asp_inactive_block := aBlock
+ ]
+
+ onAspUp: aBlock [
+ "M-ASP_UP confirm
+ Direction: M2UA -> LM
+ Purpose: ASP reports that it has received an ASP UP Acknowledgment
+ message from the SGP."
+
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ asp_up_block := aBlock
+ ]
+
+ onStateChange: aBlock [
+ "A generic callback for all state changes"
+
+ <category: 'Primitives-LayerManagemennt-ASP'>
+ on_state_change := aBlock
+ ]
+
+ deregisterLinkKey [
+ "M-LINK_KEY_DEREG Request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to de-register Link Key with SG by sending
+ DEREG REQ message."
+
+ <category: 'Primitives-LayerManagement-LinkKey'>
+ self notYetImplemented
+ ]
+
+ onLinkKeyDeregistered: aBlock [
+ "M-LINK_KEY_DEREG Confirm
+ Direction: M2UA -> LM
+ Purpose: ASP reports to LM that it has successfully received a
+ DEREG RSP message from SG."
+
+ <category: 'Primitives-LayerManagement-LinkKey'>
+ self notYetImplemented
+ ]
+
+ onLinkKeyRegistered: aBlock [
+ "M-LINK_KEY_REG Confirm
+ Direction: M2UA -> LM
+ Purpose: ASP reports to LM that it has successfully received a REG
+ RSP message from SG."
+
+ <category: 'Primitives-LayerManagement-LinkKey'>
+ self notYetImplemented
+ ]
+
+ registerLinkKey [
+ "M-LINK_KEY_REG Request
+ Direction: LM -> M2UA
+ Purpose: LM requests ASP to register Link Key with SG by sending REG
+ REQ message."
+
+ <category: 'Primitives-LayerManagement-LinkKey'>
+ self notYetImplemented
+ ]
+
+ hostname: aHostname port: aPort [
+ "Select the SCTP hostname/port for the SG to connect to"
+
+ <category: 'configuration'>
+ socket
+ hostname: aHostname;
+ port: aPort
+ ]
+
+ createAspIdentTag [
+ <category: 'm2ua-tags'>
+ ^M2UATag initWith: M2UAConstants tagAspIdent data: #(1 2 3 4)
+ ]
+
+ createIdentIntTag [
+ <category: 'm2ua-tags'>
+ ^M2UATag initWith: M2UAConstants tagIdentInt data: #(0 0 0 0)
+ ]
+
+ createInfoTag [
+ <category: 'm2ua-tags'>
+ ^M2UATag initWith: M2UAConstants tagInfo
+ data: 'Hello from Smalltalk' asByteArray
+ ]
+
+ callNotification: aBlock [
+ "Inform the generic method first, then all the others"
+
+ <category: 'private'>
+ on_state_change ifNotNil: [on_state_change value].
+ aBlock ifNotNil: [aBlock value]
+ ]
+
+ checkNextState: nextState [
+ "Check if nextState and state are compatible and if not
+ throw an exception. TODO:"
+
+ <category: 'private'>
+ self state = nextState
+ ifTrue:
+ [^self error: ('M2UA ASP already in state <1p>' expandMacrosWith: state)].
+ (self state nextPossibleStates includes: nextState)
+ ifFalse:
+ [^self error: ('M2UA ASP illegal state transition from <1p> to <2p>.'
+ expandMacrosWith: state
+ with: nextState)]
+ ]
+
+ dispatchData: aByteArray [
+ <category: 'private'>
+ | msg |
+ msg := M2UAMSG parseToClass: aByteArray.
+ msg dispatchOnAsp: self
+ ]
+
+ dispatchNotification: aBlock [
+ <category: 'private'>
+ aBlock value
+ ]
+
+ internalReset [
+ <category: 'private'>
+ self socketService: socket
+ ]
+
+ moveToState: newState [
+ <category: 'private'>
+ ((state nextPossibleStates includes: newState) or: [state = newState])
+ ifFalse:
+ [^self error: ('M2UA ASP Illegal state transition from <1p> to <2p>'
+ expandMacrosWith: state
+ with: newState)].
+
+ "TODO: general on entry, on exit"
+ state := newState
+ ]
+
+ sctpConnected [
+ <category: 'private'>
+ "The connect was issued."
+
+ | wasEstablished |
+ wasEstablished := established.
+ established := true.
+ state := M2UAAspStateDown.
+ t_ack ifNotNil: [t_ack cancel].
+ wasEstablished = true
+ ifTrue: [sctp_confirm_block ifNotNil: [sctp_confirm_block value]]
+ ifFalse: [sctp_restarted_block ifNotNil: [sctp_restarted_block value]]
+ ]
+
+ sctpReleased [
+ "The SCTP connection has been released."
+
+ <category: 'private'>
+ self moveToState: M2UAAspStateDown.
+ established = true ifFalse: [^self].
+ sctp_released_block ifNotNil: [sctp_released_block value]
+ ]
+
+ send: aMsg [
+ "Forget about what we did before"
+
+ <category: 'private'>
+ t_ack ifNotNil: [t_ack cancel].
+ t_ack := TimerScheduler instance scheduleInSeconds: 2
+ block:
+ ["Re-send the message"
+
+ self logNotice: ('<1p>:<2p> Sending message has timed out'
+ expandMacrosWith: socket hostname
+ with: socket port)
+ area: #m2ua.
+ self send: aMsg].
+ socket nextPut: aMsg toMessage asByteArray
+ ]
+
+ initialize [
+ <category: 'creation'>
+ state := M2UAAspStateDown
+ ]
+
+ socketService: aService [
+ <category: 'creation'>
+ socket := aService.
+ socket
+ onSctpConnect: [self sctpConnected];
+ onSctpReleased: [self sctpReleased];
+ onSctpData:
+ [:stream :assoc :ppid :data |
+ ppid = 2
+ ifFalse:
+ [^self logNotice: 'M2UAApplicationServerProcess expecting PPID 2.'
+ area: #m2ua].
+ self dispatchData: data]
+ ]
+
+ handleAspActiveAck: aMsg [
+ <category: 'dispatch'>
+ t_ack cancel.
+ self moveToState: M2UAAspStateActive.
+ self callNotification: asp_active_block
+ ]
+
+ handleAspDownAck: aMsg [
+ <category: 'dispatch'>
+ t_ack cancel.
+ as_state := nil.
+ self moveToState: M2UAAspStateDown.
+ self callNotification: asp_down_block
+ ]
+
+ handleAspInactiveAck: aMsg [
+ <category: 'dispatch'>
+ t_ack cancel.
+ as_state := nil.
+ self moveToState: M2UAAspStateInactive.
+ self callNotification: asp_inactive_block
+ ]
+
+ handleAspUpAck: aMsg [
+ <category: 'dispatch'>
+ t_ack cancel.
+ self moveToState: M2UAAspStateInactive.
+ self callNotification: asp_inactive_block
+ ]
+
+ handleError: aMsg [
+ "Cancel pending operations.. because something went wrong"
+
+ <category: 'dispatch'>
+ t_ack cancel.
+ error_block ifNotNil: [error_block value: aMsg]
+ ]
+
+ handleNotify: aMsg [
+ <category: 'dispatch'>
+ "Extract the status"
+
+ | tag type ident |
+ tag := aMsg findTag: M2UAConstants tagStatus.
+ tag ifNil: [^self].
+ type := (tag data ushortAt: 1) swap16.
+ ident := (tag data ushortAt: 3) swap16.
+ type = M2UAConstants ntfyKindStateChange ifTrue: [as_state := ident].
+
+ "Inform our user about it"
+ notify_block ifNotNil: [notify_block value: type value: ident]
+ ]
+
+ handleUnknownMessage: aMsg [
+ "We got something we don't know. ignore it for now."
+
+ <category: 'dispatch'>
+
+ ]
+
+ isASActive [
+ <category: 'status'>
+ ^as_state = M2UAConstants ntfyStateASActive
+ ]
+
+ isASInactive [
+ <category: 'status'>
+ ^as_state = M2UAConstants ntfyStateASInactive
+ ]
+
+ isASPending [
+ <category: 'status'>
+ ^as_state = M2UAConstants ntfyStateASPending
+ ]
+
+ state [
+ <category: 'accessing'>
+ ^state
+ ]
+]
diff --git a/m2ua/M2UAAspStateMachine.st b/m2ua/M2UAAspStateMachine.st
new file mode 100644
index 0000000..0e495e6
--- /dev/null
+++ b/m2ua/M2UAAspStateMachine.st
@@ -0,0 +1,106 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+Object subclass: M2UAAspStateMachine [
+ | state |
+
+ <category: 'OsmoNetwork-M2UA-States'>
+ <comment: 'I am the Application Server Process State machine. An
+pplication Server Process will create me to manage the state. My state
+machine is driven by calling the selectors from the events protocol.
+If you ask for an illegal state transition a DNU will be raised. Ath
+this point you should probably reset what you are doing and do proper
+error reporting.
+
+This class is currently not used!'>
+
+ M2UAAspStateMachine class >> initialState [
+ ^M2UAAspStateDown
+ ]
+
+ M2UAAspStateMachine class >> new [
+ ^(self basicNew)
+ initialize;
+ yourself
+ ]
+
+ entered: aState [
+ aState entered
+
+ "TODO notify users of the machine"
+ ]
+
+ initialize [
+ state := self class initialState on: self
+ ]
+
+ left: aState [
+ aState left
+
+ "TODO notify users of the machine"
+ ]
+
+ moveToState: aNewState [
+ | oldState |
+ oldState := state.
+ state := (aNewState new)
+ machine: self;
+ yourself.
+ self left: oldState.
+ self entered: state
+ ]
+
+ state [
+ ^state class
+ ]
+
+ aspActive: anEvent [
+ <category: 'events'>
+ state onAspActive: anEvent
+ ]
+
+ aspDown: anEvent [
+ <category: 'events'>
+ state onAspDown: anEvent
+ ]
+
+ aspInactive: anEvent [
+ <category: 'events'>
+ state onAspInactive: anEvent
+ ]
+
+ aspUp: anEvent [
+ <category: 'events'>
+ state onAspUp: anEvent
+ ]
+
+ otherAspInAsOverrides: anEvent [
+ <category: 'events'>
+ state onOtherAspInAsOverrides: anEvent
+ ]
+
+ sctpCdi: anEvent [
+ <category: 'events'>
+ state onSctpCdi: anEvent
+ ]
+
+ sctpRi: anEvent [
+ <category: 'events'>
+ state onSctpRi: anEvent
+ ]
+]
diff --git a/m2ua/M2UAExamples.st b/m2ua/M2UAExamples.st
new file mode 100644
index 0000000..feb29c4
--- /dev/null
+++ b/m2ua/M2UAExamples.st
@@ -0,0 +1,42 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+Object subclass: M2UAExamples [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ createAsp [
+ "Create a SCTP network service"
+
+ | service asp manager |
+ service := SCTPNetworkService new
+ hostname: 'localhost';
+ port: 2904;
+ yourself.
+ "Create the ASP"
+ asp := M2UAApplicationServerProcess initWith: service.
+
+ "Create a Layer Management (LM) and start it"
+ manager := M2UALayerManagement new
+ applicationServerProcess: asp;
+ targetState: M2UAAspStateActive;
+ yourself.
+ manager manage
+ ]
+]
diff --git a/m2ua/M2UALayerManagement.st b/m2ua/M2UALayerManagement.st
new file mode 100644
index 0000000..2f0e086
--- /dev/null
+++ b/m2ua/M2UALayerManagement.st
@@ -0,0 +1,127 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+Object subclass: M2UALayerManagement [
+ | targetState managedProcess |
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: 'I am taking the LayerManagement control for an M2UAApplicationServiceProcess.
+
+Currently you can tell me the ASP state this class should be in
+and I will react to to the events from the ASP.'>
+
+ applicationServerProcess: aProcess [
+ <category: 'creation'>
+ managedProcess := aProcess.
+ managedProcess
+ onSctpEstablished: [self sctpEstablished];
+ onSctpRestarted: [self sctpEstablished];
+ onError: [:msg | self m2uaError: msg];
+ onNotify: [:type :ident | self m2uaNotify: type ident: ident];
+ onAspActive: [self m2uaActive];
+ onAspInactive: [self m2uaInactive];
+ onAspDown: [self m2uaDown];
+ onAspUp: [self m2uaUp]
+ ]
+
+ manage [
+ "I begin to manage the process."
+
+ <category: 'creation'>
+ managedProcess
+ sctpRelease;
+ sctpEstablish
+ ]
+
+ targetState: aState [
+ "Use the M2UAAspState subclasses for the states"
+
+ <category: 'creation'>
+ targetState := aState
+ ]
+
+ applicationServerProcess [
+ <category: 'accessing'>
+ ^managedProcess
+ ]
+
+ m2uaActive [
+ "E.g if the target state is already reached"
+
+ <category: 'as-process-callbacks'>
+ managedProcess state = targetState ifTrue: [^self targetReached].
+ targetState = M2UAAspStateInactive
+ ifTrue: [managedProcess aspInactive]
+ ifFalse: [managedProcess aspDown]
+ ]
+
+ m2uaDown [
+ "E.g if the target state is already reached"
+
+ <category: 'as-process-callbacks'>
+ managedProcess state = targetState ifTrue: [^self targetReached].
+
+ "There is only one way forward"
+ managedProcess aspUp
+ ]
+
+ m2uaError: aMsg [
+ <category: 'as-process-callbacks'>
+ self logNotice: 'M2UA Error.' area: #m2ua
+ ]
+
+ m2uaInactive [
+ "E.g if the target state is already reached"
+
+ <category: 'as-process-callbacks'>
+ managedProcess state = targetState ifTrue: [^self targetReached].
+ targetState = M2UAAspStateActive
+ ifTrue: [managedProcess aspActive]
+ ifFalse: [managedProcess aspDown]
+ ]
+
+ m2uaNotify: type ident: ident [
+ "TODO: Check the type/ident"
+
+ <category: 'as-process-callbacks'>
+
+ ]
+
+ m2uaUp [
+ "E.g if the target state is already reached"
+
+ <category: 'as-process-callbacks'>
+ managedProcess state = targetState ifTrue: [^self targetReached].
+ targetState = M2UAAspStateActive
+ ifTrue: [managedProcess aspActive]
+ ifFalse: [managedProcess aspInactive]
+ ]
+
+ sctpEstablished [
+ "E.g if the target state is already reached"
+
+ <category: 'as-process-callbacks'>
+ managedProcess state = targetState ifTrue: [^self].
+ "There is only one way forward"
+ managedProcess aspUp
+ ]
+
+ targetReached [
+
+ ]
+]
diff --git a/m2ua/M2UAMSG.st b/m2ua/M2UAMSG.st
index 3b763aa..313555b 100644
--- a/m2ua/M2UAMSG.st
+++ b/m2ua/M2UAMSG.st
@@ -57,6 +57,41 @@ struct m2ua_parameter_hdr {
yourself.
]
+ M2UAMSG class >> copyFrom: aMsg [
+ <category: 'parsing'>
+ ^ self new
+ msgClass: aMsg msgClass;
+ msgType: aMsg msgType;
+ tags: aMsg tags;
+ yourself
+ ]
+
+ M2UAMSG class >> parseToClass: aMsg [
+ <category: 'parsing'>
+ "This will attempt to parse the message into one of the
+ available subclasses."
+
+ | rawMsg msgClasses |
+ rawMsg := self parseFrom: aMsg.
+
+ "A simple class based lookup"
+ msgClasses :=
+ {M2UAASPSMMessage.
+ M2UAASPTMMessage.
+ M2UAASPMGMTMessage}.
+ msgClasses do:
+ [:msgClass |
+ rawMsg msgClass = msgClass messageClass
+ ifTrue:
+ [msgClass allSubclassesDo: [:class |
+ class messageTag = rawMsg msgType
+ ifTrue: [^class copyFrom: rawMsg]]]].
+
+ ^self error: ('Unknown message class (<1p>) or message type (<2p>)'
+ expandMacrosWith: rawMsg msgClass
+ with: rawMsg msgType)
+ ]
+
msgClass [
<category: 'accessing'>
^ msg_class
@@ -67,6 +102,13 @@ struct m2ua_parameter_hdr {
^ msg_type
]
+ findTag: aTag [
+ "I find a tag with a tag identifier"
+
+ <category: 'accessing'>
+ ^self findTag: aTag ifAbsent: [nil]
+ ]
+
findTag: aTag ifAbsent: aBlock [
"I find a tag with a tag identifier"
<category: 'accessing'>
@@ -85,42 +127,61 @@ struct m2ua_parameter_hdr {
]
parseFrom: aStream [
- | version spare len end |
- <category: 'parsing'>
-
- version := aStream next.
- version = M2UAConstants version ifFalse: [
- self logError:
- ('M2UA version is wrong <1p>.' expandMacrosWith: version) area: #m2ua.
- self error: ('M2UA version is wrong <1p>.' expandMacrosWith: version).
- ].
-
- spare := aStream next.
- spare = M2UAConstants spare ifFalse: [
- self logError: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare) area: #m2ua.
- self error: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare).
- ].
-
- msg_class := aStream next.
- msg_type := aStream next.
+ <category: 'parsing'>
+ | len |
+ self parseVersion: aStream.
+ self parseSpare: aStream.
+ msg_class := aStream next.
+ msg_type := aStream next.
+ len := self parseLength: aStream.
+ tags := self parseTags: aStream to: aStream position + len - 8
+ ]
- len := ((aStream next: 4) uintAt: 1) swap32.
- aStream size - aStream position < (len - 8) ifTrue: [
- self logError: ('M2UA length is not plausible <1p> <2p>.'
- expandMacrosWith: len with: aStream size - aStream position)
- area: #m2ua.
- self error: ('M2UA length is not plausible <1p> <2p>.'
- expandMacrosWith: len with: aStream size - aStream position).
- ].
+ parseLength: aStream [
+ <category: 'parsing'>
+ | len |
+ len := ((aStream next: 4) uintAt: 1) swap32.
+ aStream size - aStream position < (len - 8)
+ ifTrue:
+ [self
+ logError: ('M2UA length is not plausible <1p> <2p>.' expandMacrosWith: len
+ with: aStream size - aStream position)
+ area: #m2ua.
+ self
+ error: ('M2UA length is not plausible <1p> <2p>.' expandMacrosWith: len
+ with: aStream size - aStream position)].
+ ^len
+ ]
- tags := OrderedCollection new.
- end := aStream position + len - 8.
+ parseSpare: aStream [
+ <category: 'parsing'>
+ | spare |
+ spare := aStream next.
+ spare = M2UAConstants spare
+ ifFalse:
+ [self logError: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare)
+ area: #m2ua.
+ self error: ('M2UA spare is wrong <1p>.' expandMacrosWith: spare)]
+ ]
- [aStream position < end] whileTrue: [
- tags add: (M2UATag fromStream: aStream)
- ].
+ parseTags: aStream to: end [
+ <category: 'parsing'>
+ tags := OrderedCollection new.
+ [aStream position < end]
+ whileTrue: [tags add: (M2UATag fromStream: aStream)].
+ ^tags
]
+ parseVersion: aStream [
+ <category: 'parsing'>
+ | version |
+ version := aStream next.
+ version = M2UAConstants version
+ ifFalse:
+ [self logError: ('M2UA version is wrong <1p>.' expandMacrosWith: version)
+ area: #m2ua.
+ self error: ('M2UA version is wrong <1p>.' expandMacrosWith: version)]
+ ]
addTag: aTag [
<category: 'encoding'>
self tags add: aTag.
@@ -143,5 +204,30 @@ struct m2ua_parameter_hdr {
aMsg putLen32: tag_data size + 8.
aMsg putByteArray: tag_data.
]
+
+ class: aClass [
+ <category: 'creation'>
+ msg_class := aClass
+ ]
+
+ msgClass: aClass [
+ <category: 'creation'>
+ self class: aClass
+ ]
+
+ msgType: aType [
+ <category: 'creation'>
+ msg_type := aType
+ ]
+
+ tags: aTags [
+ <category: 'creation'>
+ tags := aTags
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleUnknownMessage: self
+ ]
]
diff --git a/m2ua/M2UAMessages.st b/m2ua/M2UAMessages.st
new file mode 100644
index 0000000..e9f4a16
--- /dev/null
+++ b/m2ua/M2UAMessages.st
@@ -0,0 +1,217 @@
+"
+ (C) 2011-2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+M2UAMSG subclass: M2UAASPSMMessage [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: 'Application Server Process State Maintenance (ASPSM) messages'>
+
+ M2UAASPSMMessage class >> messageClass [
+ ^M2UAConstants clsASPSM
+ ]
+]
+
+M2UAMSG subclass: M2UAASPTMMessage [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAASPTMMessage class >> messageClass [
+ ^M2UAConstants clsASPTM
+ ]
+]
+
+M2UAMSG subclass: M2UAASPMGMTMessage [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAASPMGMTMessage class >> messageClass [
+ ^M2UAConstants clsMgmt
+ ]
+]
+
+M2UAASPSMMessage subclass: M2UAApplicationServerProcessHeartbeatAck [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessHeartbeatAck class >> messageTag [
+ ^M2UAConstants aspsmBeatAck
+ ]
+]
+
+M2UAASPSMMessage subclass: M2UAApplicationServerProcessDown [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessDown class >> messageTag [
+ ^M2UAConstants aspsmDown
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspDown: self
+ ]
+]
+
+M2UAASPSMMessage subclass: M2UAApplicationServerProcessHeartbeat [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessHeartbeat class >> messageTag [
+ ^M2UAConstants aspsmBeat
+ ]
+]
+
+M2UAASPSMMessage subclass: M2UAApplicationServerProcessDownAck [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessDownAck class >> messageTag [
+ ^M2UAConstants aspsmDownAck
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspDownAck: self
+ ]
+]
+
+M2UAASPSMMessage subclass: M2UAApplicationServerProcessUp [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessUp class >> messageTag [
+ ^M2UAConstants aspsmUp
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspUp: self
+ ]
+]
+
+M2UAASPTMMessage subclass: M2UAApplicationServerProcessInactiveAck [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessInactiveAck class >> messageTag [
+ ^M2UAConstants asptmInactivAck
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspInactiveAck: self
+ ]
+]
+
+M2UAASPTMMessage subclass: M2UAApplicationServerProcessActive [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessActive class >> messageTag [
+ ^M2UAConstants asptmActiv
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspActive: self
+ ]
+]
+
+M2UAASPTMMessage subclass: M2UAApplicationServerProcessInactive [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessInactive class >> messageTag [
+ ^M2UAConstants asptmInactiv
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspInactive: self
+ ]
+]
+
+M2UAASPMGMTMessage subclass: M2UAApplicationServerProcessNotify [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessNotify class >> messageTag [
+ ^M2UAConstants mgmtNtfy
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleNotify: self
+ ]
+]
+
+M2UAASPMGMTMessage subclass: M2UAApplicationServerProcessError [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessError class >> messageTag [
+ ^M2UAConstants mgmtError
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleError: self
+ ]
+]
+
+M2UAASPTMMessage subclass: M2UAApplicationServerProcessActiveAck [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessActiveAck class >> messageTag [
+ ^M2UAConstants asptmActivAck
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspActiveAck: self
+ ]
+]
+
+M2UAASPSMMessage subclass: M2UAApplicationServerProcessUpAck [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: nil>
+
+ M2UAApplicationServerProcessUpAck class >> messageTag [
+ ^M2UAConstants aspsmUpAck
+ ]
+
+ dispatchOnAsp: anAsp [
+ <category: 'm2ua-asp-dispatch'>
+ anAsp handleAspUpAck: self
+ ]
+]
diff --git a/m2ua/M2UAStates.st b/m2ua/M2UAStates.st
index ed9f1ae..dc26c85 100644
--- a/m2ua/M2UAStates.st
+++ b/m2ua/M2UAStates.st
@@ -42,6 +42,7 @@ STInST.RBProgramNodeVisitor subclass: M2UAStateMachineVisitor [
Object subclass: M2UAStateBase [
+ | machine |
<category: 'OsmoNetwork-M2UA-States'>
<comment: 'I am the base class of all M2UA state machines. My direct subclasses are state machines and their subclasses are the individual states that make up the statemachine.'>
@@ -82,6 +83,31 @@ Object subclass: M2UAStateBase [
nextPutAll: '}';
contents
]
+
+ M2UAStateBase class >> on: aMachine [
+ "Create a new state for a machine"
+
+ ^self new
+ machine: aMachine;
+ yourself
+ ]
+
+ entered [
+ "The state has been entered"
+ ]
+
+ left [
+ "The state has been left"
+ ]
+
+ machine: aMachine [
+ machine := aMachine
+ ]
+
+ moveToState: aNewState [
+ <category: 'transition'>
+ machine moveToState: aNewState
+ ]
]
@@ -147,6 +173,10 @@ M2UAStateBase subclass: M2UAAspState [
<category: 'OsmoNetwork-M2UA-States'>
<comment: 'I am the base class of the ASP State Machine from RFC 3331 on Page 61.'>
+
+ M2UAAspState class >> nextPossibleStates [
+ ^self subclassResponsibility
+ ]
]
@@ -156,6 +186,10 @@ M2UAAspState subclass: M2UAAspStateActive [
<category: 'OsmoNetwork-M2UA-States'>
<comment: nil>
+ M2UAAspStateActive class >> nextPossibleStates [
+ ^ {M2UAAspStateInactive. M2UAAspStateDown}
+ ]
+
onAspDown: anEvent [
<category: 'state-changes'>
self moveToState: M2UAAspStateDown
@@ -189,6 +223,10 @@ M2UAAspState subclass: M2UAAspStateDown [
<category: 'OsmoNetwork-M2UA-States'>
<comment: nil>
+ M2UAAspStateDown class >> nextPossibleStates [
+ ^{M2UAAspStateInactive}
+ ]
+
onAspUp: anEvent [
<category: 'state-changes'>
^self moveToState: M2UAAspStateInactive
@@ -202,6 +240,10 @@ M2UAAspState subclass: M2UAAspStateInactive [
<category: 'OsmoNetwork-M2UA-States'>
<comment: nil>
+ M2UAAspStateInactive class >> nextPossibleStates [
+ ^ {M2UAAspStateActive. M2UAAspStateDown}
+ ]
+
onAspActive: anEvent [
<category: 'state-changes'>
^self moveToState: M2UAAspStateActive
diff --git a/m2ua/M2UATerminology.st b/m2ua/M2UATerminology.st
new file mode 100644
index 0000000..6c1ae72
--- /dev/null
+++ b/m2ua/M2UATerminology.st
@@ -0,0 +1,44 @@
+"
+ (C) 2011-2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+Object subclass: M2UATerminology [
+
+ <category: 'OsmoNetwork-M2UA'>
+ <comment: 'I attempt to help with the terminology for M2UA.
+
+M2UA is defined in IETF RFC 3331 and is actually from a family
+of closely related RFCs for M3UA, SUA, M2PA.
+
+The whole idea is that one can adapt the M2UA layer from the classlic
+E1/T1 timeslots to the more modern SCTP (SIGTRAN). MTP3 and above will
+not notice the difference.
+
+The communication for M2UA is between two systems, both should be
+configurable as either a client or server (listening for incoming SCTP
+connections).
+
+In general the communication is between a Signalling Gateway
+(SG) and a Media Gateway Controller (MGC). In our world the MGC
+would is the MSC/HLR/VLR/AuC.
+
+What makes things complicated is the cardinality of systems. There is
+an Application Server (AS), this can have multiple Application Server
+Processes (ASP) for one or multiple MTP links. While the RFC onlys
+says that the SG should the list of ASs in practice both ends need to
+do it.'>
+]
diff --git a/m2ua/M2UATests.st b/m2ua/M2UATests.st
new file mode 100644
index 0000000..4f6def2
--- /dev/null
+++ b/m2ua/M2UATests.st
@@ -0,0 +1,222 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+"
+
+Object subclass: M2UAASMock [
+ | socket |
+
+ <category: 'OsmoNetwork-M2UA-Tests'>
+ <comment: 'A simple mock'>
+
+ socketService: aSocket [
+ <category: 'creation'>
+ socket := aSocket
+ ]
+
+ handleAspActive: aMsg [
+ <category: 'dispatch'>
+ | ret |
+ ret := M2UAMSG new
+ msgClass: M2UAConstants clsASPTM;
+ msgType: M2UAConstants asptmActivAck;
+ yourself.
+ socket sendToAsp: ret toMessage asByteArray
+ ]
+
+ handleAspDown: aMsg [
+ <category: 'dispatch'>
+ | ret |
+ ret := M2UAMSG new
+ msgClass: M2UAConstants clsASPSM;
+ msgType: M2UAConstants aspsmDownAck;
+ yourself.
+ socket sendToAsp: ret toMessage asByteArray
+ ]
+
+ handleAspInactive: aMsg [
+ <category: 'dispatch'>
+ | ret |
+ ret := M2UAMSG new
+ msgClass: M2UAConstants clsASPTM;
+ msgType: M2UAConstants asptmInactivAck;
+ yourself.
+ socket sendToAsp: ret toMessage asByteArray
+ ]
+
+ handleAspUp: aMsg [
+ <category: 'dispatch'>
+ | ret |
+ ret := M2UAMSG new
+ msgClass: M2UAConstants clsASPSM;
+ msgType: M2UAConstants aspsmUpAck;
+ yourself.
+ socket sendToAsp: ret toMessage asByteArray
+ ]
+
+ onData: aData [
+ | msg |
+ msg := M2UAMSG parseToClass: aData.
+ msg dispatchOnAsp: self
+ ]
+]
+
+Object subclass: SCTPNetworkServiceMock [
+ | on_connect on_released on_data as asp |
+
+ <category: 'OsmoNetwork-M2UA-Tests'>
+ <comment: 'I mock SCTPand directly connect an AS with an ASP.'>
+
+ onSctpConnect: aBlock [
+ <category: 'notification'>
+ on_connect := aBlock
+ ]
+
+ applicationServer: anAs [
+ <category: 'creation'>
+ as := anAs
+ ]
+
+ applicationServerProcess: anAsp [
+ <category: 'creation'>
+ asp := anAsp
+ ]
+
+ onSctpData: aBlock [
+ <category: 'creation'>
+ on_data := aBlock
+ ]
+
+ onSctpReleased: aBlock [
+ <category: 'creation'>
+ on_released := aBlock
+ ]
+
+ hostname [
+ <category: 'management'>
+ ^'localhost'
+ ]
+
+ port [
+ <category: 'management'>
+ ^0
+ ]
+
+ start [
+ "Nothing"
+
+ <category: 'management'>
+ on_connect value
+ ]
+
+ stop [
+ <category: 'management'>
+ on_released value
+ ]
+
+ nextPut: aMsg [
+ as onData: aMsg
+ ]
+
+ sendToAsp: aMsg [
+ on_data
+ value: nil
+ value: nil
+ value: 2
+ value: aMsg
+ ]
+]
+
+TestCase subclass: M2UAApplicationServerProcessTest [
+
+ <comment: 'A M2UAApplicationServerProcessTest is a test class for testing the behavior of M2UAApplicationServerProcess'>
+ <category: 'OsmoNetwork-M2UA-Tests'>
+
+ testCreation [
+ | asp |
+ asp := M2UAApplicationServerProcess new
+ onAspActive: [];
+ onAspDown: [];
+ onAspInactive: [];
+ onAspUp: [];
+ onStateChange: [];
+ onError: [:msg | ];
+ onNotify: [:type :ident | ];
+ onSctpEstablished: [];
+ onSctpReleased: [];
+ onSctpRestarted: [];
+ onSctpStatus: [];
+ yourself
+ ]
+
+ testStateTransitions [
+ | mock as asp |
+ mock := SCTPNetworkServiceMock new.
+ as := M2UAASMock new
+ socketService: mock;
+ yourself.
+ asp := M2UAApplicationServerProcess initWith: mock.
+ mock
+ applicationServer: as;
+ applicationServerProcess: asp.
+
+ "This works as the mock will handle this synchronously"
+ self assert: asp state = M2UAAspStateDown.
+ asp
+ sctpEstablish;
+ aspUp.
+ self assert: asp state = M2UAAspStateInactive.
+
+ "Now bring it down and up again"
+ asp aspDown.
+ self assert: asp state = M2UAAspStateDown.
+ asp
+ aspUp;
+ aspActive.
+ self assert: asp state = M2UAAspStateActive.
+ asp aspDown.
+ self assert: asp state = M2UAAspStateDown.
+ asp
+ aspUp;
+ aspActive;
+ aspInactive.
+ self assert: asp state = M2UAAspStateInactive.
+ asp sctpRelease.
+ self assert: asp state = M2UAAspStateDown
+ ]
+]
+
+TestCase subclass: M2UAAspStateMachineTest [
+
+ <comment: 'A M2UAAspStateMachineTest is a test class for testing the behavior of M2UAAspStateMachine'>
+ <category: 'OsmoNetwork-M2UA-Tests'>
+
+ testLegalTransitions [
+ | machine |
+ machine := M2UAAspStateMachine new.
+ self assert: machine state = M2UAAspStateDown.
+ machine aspUp: 'Link is up'.
+ self assert: machine state = M2UAAspStateInactive.
+ machine aspActive: 'Active'.
+ self assert: machine state = M2UAAspStateActive.
+ machine aspInactive: 'Inactive'.
+ self assert: machine state = M2UAAspStateInactive.
+ machine aspActive: 'Active'.
+ self assert: machine state = M2UAAspStateActive.
+ machine sctpCdi: 'Connection is gone'.
+ self assert: machine state = M2UAAspStateDown
+ ]
+]
diff --git a/package.xml b/package.xml
index 84165c7..eb6eb1f 100644
--- a/package.xml
+++ b/package.xml
@@ -9,6 +9,7 @@
<prereq>Parser</prereq>
<filein>core/Extensions.st</filein>
+ <filein>core/ExtensionsGST.st</filein>
<filein>core/MessageStructure.st</filein>
<filein>core/MessageBuffer.st</filein>
<filein>core/LogAreas.st</filein>
@@ -28,10 +29,21 @@
<filein>sccp/SCCPGlobalTitleTranslation.st</filein>
<filein>mtp3/MTP3Messages.st</filein>
<filein>ua/XUA.st</filein>
+
<filein>m2ua/M2UAConstants.st</filein>
<filein>m2ua/M2UAStates.st</filein>
<filein>m2ua/M2UATag.st</filein>
<filein>m2ua/M2UAMSG.st</filein>
+ <filein>m2ua/M2UAMessages.st</filein>
+ <filein>m2ua/M2UAStates.st</filein>
+ <filein>m2ua/M2UAAspStateMachine.st</filein>
+ <filein>m2ua/M2UAApplicationServerProcess.st</filein>
+ <filein>m2ua/M2UALayerManagement.st</filein>
+ <filein>m2ua/M2UATerminology.st</filein>
+ <filein>m2ua/M2UAExamples.st</filein>
+
+
+
<filein>osmo/LogAreaOsmo.st</filein>
<filein>osmo/OsmoUDPSocket.st</filein>
<filein>osmo/OsmoCtrlLogging.st</filein>
@@ -48,13 +60,16 @@
<sunit>Osmo.IPAGSTTests</sunit>
<sunit>Osmo.IPAMsgTests</sunit>
<sunit>Osmo.MessageBufferTest</sunit>
- <sunit>Osmo.M2UAMSGTests</sunit>
<sunit>Osmo.ISUPGeneratedTest</sunit>
<sunit>Osmo.OsmoUDPSocketTest</sunit>
<sunit>Osmo.TLVDescriptionTest</sunit>
<sunit>Osmo.CtrlGrammarTest</sunit>
<sunit>Osmo.CtrlParserTest</sunit>
+ <sunit>Osmo.M2UAMSGTests</sunit>
+ <sunit>Osmo.M2UAApplicationServerProcessTest</sunit>
+ <sunit>Osmo.M2UAAspStateMachineTest</sunit>
+
<sunit>Osmo.MTP3LabelTest</sunit>
<sunit>Osmo.MTP3SLTAMSGTest</sunit>
<sunit>Osmo.MTP3SLTMMSGTest</sunit>
@@ -66,6 +81,7 @@
<filein>isup/ISUPTests.st</filein>
<filein>ipa/IPATests.st</filein>
<filein>osmo/OsmoCtrlGrammarTest.st</filein>
+ <filein>m2ua/M2UATests.st</filein>
<filein>mtp3/MTP3MessagesTests.st</filein>
</test>
</package>