From 3a20a8bcb27ab8ec474b54bb5bfd0cd8539bfe83 Mon Sep 17 00:00:00 2001 From: mullerj Date: Thu, 9 Feb 2023 11:49:33 -0500 Subject: [PATCH 01/29] .NET Extension Design (#5) * Designed .NET classes from Java Co-authored-by: Sergiy * Added NormApi static class * Started on api uml Co-authored-by: mullerj Co-authored-by: Sylvester Freeman III * Add static methods to api uml * add static method to normapi uml * Add enums * Changes * Overload methods with defaults * type marshalling * change handles to in nuint * marshall more handles * Removed in/ref and reformatted. Co-authored-by: mullerj * Removed duplicate enums * Removed NormEvent from NormApi * Fixed typo * design NormInstance methods Co-authored-by: mullerj * Fixed CreateInstance method * Changed handles to long * Change id types to long * Remove condition checks that do nothing * Removed UserData functions * Removed NormAlloc() and NormFree() * Cleaned up some API methods * Fix misspelling Co-authored-by: Sylvester Freeman III * Design NormSession methods Co-authored-by: Sylvester Freeman III * Added missing comma * Uml for enqueue Co-authored-by: mullerj * Updated design based on enqueue test * Updated EnqueueFile sequence diagram Co-authored-by: Sylvester Freeman III * Moved sequence diagrams * Added constants to NormApi * Removed method level sequence diagrams --------- Co-authored-by: Sergiy Co-authored-by: syermak Co-authored-by: mullerj Co-authored-by: Sylvester Freeman III Co-authored-by: Sergiy <47069243+SergiyYermak@users.noreply.github.com> Co-authored-by: Sylvester Freeman III --- .../Navy/Nrl/Norm/Enums/NormAckingStatus.puml | 8 + .../Navy/Nrl/Norm/Enums/NormEventType.puml | 33 ++++ .../Mil/Navy/Nrl/Norm/Enums/NormFecType.puml | 7 + .../Navy/Nrl/Norm/Enums/NormFlushMode.puml | 7 + .../Navy/Nrl/Norm/Enums/NormNackingMode.puml | 7 + .../Navy/Nrl/Norm/Enums/NormObjectType.puml | 8 + .../Navy/Nrl/Norm/Enums/NormProbingMode.puml | 7 + .../Nrl/Norm/Enums/NormRepairBoundary.puml | 6 + .../Navy/Nrl/Norm/Enums/NormSyncPolicy.puml | 7 + .../Nrl/Norm/Enums/NormTrackingStatus.puml | 9 + .../Navy/Nrl/Norm/IO/INormEventListener.puml | 5 + .../Mil/Navy/Nrl/Norm/IO/NormInputStream.puml | 33 ++++ .../Navy/Nrl/Norm/IO/NormOutputStream.puml | 40 ++++ .../Nrl/Norm/IO/StreamBreakException.puml | 6 + .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 176 ++++++++++++++++++ .../design/Mil/Navy/Nrl/Norm/NormData.puml | 7 + .../design/Mil/Navy/Nrl/Norm/NormEvent.puml | 14 ++ .../design/Mil/Navy/Nrl/Norm/NormFile.puml | 8 + .../Mil/Navy/Nrl/Norm/NormInstance.puml | 21 +++ .../design/Mil/Navy/Nrl/Norm/NormNode.puml | 18 ++ .../design/Mil/Navy/Nrl/Norm/NormObject.puml | 17 ++ .../design/Mil/Navy/Nrl/Norm/NormSession.puml | 70 +++++++ .../design/Mil/Navy/Nrl/Norm/NormStream.puml | 17 ++ .../design/SequenceDiagrams/NormFileSend.puml | 49 +++++ 24 files changed, 580 insertions(+) create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormTrackingStatus.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml create mode 100644 src/dotnet/design/SequenceDiagrams/NormFileSend.puml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml new file mode 100644 index 00000000..f31c80ee --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml @@ -0,0 +1,8 @@ +@startuml +enum NormAckingStatus { + NORM_ACK_INVALID + NORM_ACK_FAILURE + NORM_ACK_PENDING + NORM_ACK_SUCCESS +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml new file mode 100644 index 00000000..71dd8fb2 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml @@ -0,0 +1,33 @@ +@startuml +enum NormEventType { + NORM_EVENT_INVALID + NORM_TX_QUEUE_VACANCY + NORM_TX_QUEUE_EMPTY + NORM_TX_FLUSH_COMPLETED + NORM_TX_WATERMARK_COMPLETED + NORM_TX_CMD_SENT + NORM_TX_OBJECT_SENT + NORM_TX_OBJECT_PURGED + NORM_TX_RATE_CHANGED + NORM_LOCAL_SENDER_CLOSED + NORM_REMOTE_SENDER_NEW + NORM_REMOTE_SENDER_RESET + NORM_REMOTE_SENDER_ADDRESS + NORM_REMOTE_SENDER_ACTIVE + NORM_REMOTE_SENDER_INACTIVE + NORM_REMOTE_SENDER_PURGED + NORM_RX_CMD_NEW + NORM_RX_OBJECT_NEW + NORM_RX_OBJECT_INFO + NORM_RX_OBJECT_UPDATED + NORM_RX_OBJECT_COMPLETED + NORM_RX_OBJECT_ABORTED + NORM_RX_ACK_REQUEST + NORM_GRTT_UPDATED + NORM_CC_ACTIVE + NORM_CC_INACTIVE + NORM_ACKING_NODE_NEW + NORM_SEND_ERROR + NORM_USER_TIMEOUT +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml new file mode 100644 index 00000000..4b55669b --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml @@ -0,0 +1,7 @@ +@startuml +enum NormFecType { + RS + RS8 + SB +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml new file mode 100644 index 00000000..63f7b1a8 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml @@ -0,0 +1,7 @@ +@startuml +enum NormFlushMode { + NORM_FLUSH_NONE + NORM_FLUSH_PASSIVE + NORM_FLUSH_ACTIVE +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml new file mode 100644 index 00000000..41f021ac --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml @@ -0,0 +1,7 @@ +@startuml +enum NormNackingMode { + ORM_NACK_NONE + NORM_NACK_INFO_ONLY + NORM_NACK_NORMAL +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml new file mode 100644 index 00000000..00e2dd84 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml @@ -0,0 +1,8 @@ +@startuml +enum NormObjectType { + NORM_OBJECT_NONE + NORM_OBJECT_DATA + NORM_OBJECT_FILE + NORM_OBJECT_STREAM +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml new file mode 100644 index 00000000..004ada72 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml @@ -0,0 +1,7 @@ +@startuml +enum NormProbingMode { + NORM_PROBE_NONE + NORM_PROBE_PASSIVE + NORM_PROBE_ACTIVE +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml new file mode 100644 index 00000000..b97df403 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml @@ -0,0 +1,6 @@ +@startuml +enum NormRepairBoundary { + NORM_BOUNDARY_BLOCK + NORM_BOUNDARY_OBJECT +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml new file mode 100644 index 00000000..3bb1cf54 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml @@ -0,0 +1,7 @@ +@startuml +enum NormSyncPolicy { + NORM_SYNC_CURRENT + NORM_SYNC_STREAM + NORM_SYNC_ALL +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormTrackingStatus.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormTrackingStatus.puml new file mode 100644 index 00000000..048d1e48 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormTrackingStatus.puml @@ -0,0 +1,9 @@ +@startuml +enum NormTrackingStatus +{ + NORM_TRACK_NONE + NORM_TRACK_RECEIVERS + NORM_TRACK_SENDERS + NORM_TRACK_ALL +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml new file mode 100644 index 00000000..ba923b3a --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml @@ -0,0 +1,5 @@ +@startuml +interface INormEventListener { + NormEventOccurred(normEvent:NormEvent) : void +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml new file mode 100644 index 00000000..dc3434d7 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml @@ -0,0 +1,33 @@ +@startuml +class NormInputStream { + - _normInstance : NormInstance + - _normSession : NormSession + - _normStream : NormStream + - _normEventListeners : List + - _closed : bool + - _closeLock : object + - _bufferIsEmpty : bool + - _receivedEof : bool + + NormInputStream(address:string, port:int) + + OpenDebugLog(filename:string) : void + + SetDebugLevel(level:int) : void + + SetMessageTrace(messageTrace:bool) : void + + SetMulticastInterface(multicastInterface: string) : void + + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool) + + SetTtl(ttl:byte) : void + + SetTos(tos:byte) : void + + SetSilentReceiver(silent:bool, maxDelay:int) : void + + SetDefaultUnicastNack(defaultUnicastNack:bool) : void + + SeekMsgStart() : void + + AddNormEventListener(normEventListener:INormEventListener) : void + + RemoveNormEventListener(normEventListener:INormEventListener) : void + - FireNormEventOccured(normEvent:NormEvent) : void + + Open(bufferSpace:long) : void + + <> Dispose() : void + + IsClosed : bool <> + + Read() : int + + <> Read(buffer:byte[], offset:int, length:int) : int + - ProcessEvent() : void +} +Stream <|-- NormInputStream +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml new file mode 100644 index 00000000..9dffa7b0 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml @@ -0,0 +1,40 @@ +@startuml +class NormOutputStream { + - _normInstance : NormInstance + - _normSession : NormSession + - _normStream : NormStream + - _normEventListeners : List + - _closed : bool + - _closeLock : object + - _bufferIsFull : bool + + NormOutputStream(address:string, port:int) + + OpenDebugLog(filename:string) : void + + CloseDebugLog() : void + + SetDebugLevel(level:int) : void + + SetMessageTrace(messageTrace:bool) : void + + SetMulticastInterface(multicastInterface:string) : void + + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool): void + + SetTtl(ttl:byte) : void + + SetTos(tos:byte) : void + + SetCongestionControl(ccEnabled:bool, ccAdjustRate: bool) : void + + SetTxRateBounds(rateMin:double, rateMax:double) : void + + TxRate:double <> <> + + GrttEstimate:double <> <> + + SetGroupSize(groupSize:long) : void + + SetAutoParity(autoParity:short) : void + + SetBackoffFactor(backoffFactor:double) : void + + SetAutoFlush(flushMode:NormFlushMode) : void + + SetPushEnable(pushEnable:bool) : void + + MarkEom() : void + + AddNormEventListener(normEventListener:INormEventListener) : void + + RemoveNormEventListener(normEventListener:INormEventListener): void + - FireNormEventOccured(normEvent:NormEvent) : void + + Open(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, repairWindow:long) : void + + <> Dispose() : void + + IsClosed : bool <> + + Write(b:int) : void + + <> Write(buffer:byte[], offset:int, count:int) + - ProcessEvent() : void +} +Stream <|-- NormOutputStream +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml new file mode 100644 index 00000000..6ae40082 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml @@ -0,0 +1,6 @@ +@startuml +class StreamBreakException { + + StreamBreakException(message:string) +} +IOException <|-- StreamBreakException +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml new file mode 100644 index 00000000..d6ac2674 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -0,0 +1,176 @@ +@startuml +class NormApi +{ + + <> NORM_OBJECT_INVALID : int = 0 + + <> FILENAME_MAX : int = 260 + + <> NORM_LIBRARY : string = "norm" + + {static} NormGetVersion(major:int, minor:int, patch:int) : int + + {static} NormCreateInstance() : long + + {static} NormCreateInstance(priorityBoost:bool) : long + + {static} NormDestroyInstance(instanceHandle:long) : void + + {static} NormStopInstance(instanceHandle:long) : void + + {static} NormRestartInstance(instanceHandle:long) : bool + + {static} NormSuspendInstance(instanceHandle:long) : bool + + {static} NormResumeInstance(instanceHandle:long) : void + + {static} NormSetCacheDirectory(instanceHandle:long, cachePath:string) : bool + + {static} NormGetNextEvent(instanceHandle:long, theEvent:NormEvent) : bool + + {static} NormGetNextEvent(instanceHandle:long, theEvent:NormEvent, waitForEvent:bool) : bool + + {static} NormGetDescriptor(instanceHandle:long) : NormDescriptor + + {static} NormCreateSession(instanceHandle:long, sessionAddress:string, sessionPort:int, localNodeId:long) : long + + {static} NormDestroySession(sessionHandle:long) : void + + {static} NormGetInstance(sessionHandle:long) : long + + {static} NormIsUnicastAddress(address:string) : bool + + {static} NormSetUserTimer(sessionHandle:long, seconds:double) : void + + {static} NormCancelUserTimer(sessionHandle:long) : void + + {static} NormGetLocalNodeId(sessionHandle:long) : long + + {static} NormGetAddress(sessionHandle:long, addrBuffer:string, bufferLen:uint) : bool + + {static} NormGetAddress(sessionHandle:long, addrBuffer:string, bufferLen:uint, port:ushort) : bool + + {static} NormGetRxPort(sessionHandle:long) : ushort + + {static} NormSetTxPort(sessionHandle:long, txPortNumber:ushort) : bool + + {static} NormSetTxPort(sessionHandle:long, txPortNumber:ushort, enableReuse:bool, txBindAddress:string) : bool + + {static} NormGetTxPort(sessionHandle:long) : ushort + + {static} NormSetTxOnly(sessionHandle:long, txOnly:bool) : void + + {static} NormSetTxOnly(sessionHandle:long, txOnly:bool, connectToSessionAddress:bool) : void + + {static} NormLimitObjectInfo(sessionHandle:long, state:bool) : void + + {static} NormPresetObjectInfo(sessionHandle:long, objectSize:ulong, segmentSize:ushort, numData:ushort, numParity:ushort) : bool + + {static} NormSetId(sessionHandle:long, normId:long) : void + + {static} NormChangeDestination(sessionHandle:long, sessionAddress:string, sessionPort:ushort) : bool + + {static} NormChangeDestination(sessionHandle:long, sessionAddress:string, sessionPort:ushort, connectToSessionAddress:bool) : bool + + {static} NormSetServerListner(sessionHandle:long, state:bool) : void + + {static} NormTransferSender(sessionHandle:long, sender:long) : bool + + {static} NormSetRxPortReuse(sessionHandle:long, enableReuse:bool) : void + + {static} NormSetRxPortReuse(sessionHandle:long, enableReuse:bool, rxBindAddress:string, senderAddress:string, senderPort:ushort) : void + + {static} NormGetRxBindAddress(sessionHandle:long, addr:string, addrLen:uint, port:ushort) : bool + + {static} NormSetEcnSupport(sessionHandle:long, ecnEnable:bool) : void + + {static} NormSetEcnSupport(sessionHandle:long, ecnEnable:bool, ignoreLoss:bool, tolerateLoss:bool) : void + + {static} NormSetMulticastInterface(sessionHandle:long, interfaceName:string) : bool + + {static} NormSetSSM(sessionHandle:long, sourceAddress:string) : bool + + {static} NormSetTTL(sessionHandle:long, ttl:byte) : bool + + {static} NormSetTOS(sessionHandle:long, tos:byte) : bool + + {static} NormSetLoopback(sessionHandle:long, loopback:bool) : bool + + {static} NormSetMulticastLoopback(sessionHandle:long, loopback:bool) : bool + + {static} NormSetFragmentation(sessionHandle:long, fragmentation:bool) : bool + + {static} NormSetMessageTrace(sessionHandle:long, state:bool) : void + + {static} NormSetTxLoss(sessionHandle:long, precent:double) : void + + {static} NormSetRxLoss(sessionHandle:long, precent:double) : void + + {static} NormOpenDebugLog(instanceHandle:long, path:string) : bool + + {static} NormCloseDebugLog(instanceHandle:long) : void + + {static} NormOpenDebugPipe(instanceHandle:long, pipeName:string) : bool + + {static} NormCloseDebugPipe(instanceHandle:long) : void + + {static} NormSetDebugLevel(level:int) : void + + {static} NormGetDebugLevel() : int + + {static} NormSetReportInterval(sessionHandle:long, interval:double) : void + + {static} NormGetReportInterval(sessionHandle:long) : double + + {static} NormGetRandomSessionId() : int + + {static} NormStartSender(sessionHandle:long, instanceId:int, bufferSpace:long, segmentSize:int, numData:short, numParity:short) : bool + + {static} NormStartSender(sessionHandle:long, instanceId:int, bufferSpace:long, segmentSize:int, numData:short, numParity:short, fecId:NormFecType) : bool + + {static} NormStopSender(sessionHandle:long) : void + + {static} NormSetTxRate(sessionHandle:long, bitsPerSecond:double) : void + + {static} NormGetTxRate(sessionHandle:long) : double + + {static} NormSetTxSocketBuffer(sessionHandle:long, bufferSize:uint) : bool + + {static} NormSetFlowControl(sessionHandle:long, flowControlFactor:double) : void + + {static} NormSetCongestionControl(sessionHandle:long, enable:bool) : void + + {static} NormSetCongestionControl(sessionHandle:long, enable:bool, adjustRate:bool) : void + + {static} NormSetTxRateBounds(sessionHandle:long, rateMin:double, rateMax:double) : void + + {static} NormSetTxCacheBounds(sessionHandle:long, sizeMax:long, countMin:long, countMax:long) : void + + {static} NormSetAutoParity(sessionHandle:long, autoParity:byte) : void + + {static} NormSetGrttEstimate(sessionHandle:long, grttEstimate:double) : void + + {static} NormGetGrttEstimate(sessionHandle:long) : double + + {static} NormSetGrttMax(sessionHandle:long, grttMax:double) : void + + {static} NormSetGrttProbingMode(sessionHandle:long, probingMode:NormProbingMode) : void + + {static} NormSetGrttProbingInterval(sessionHandle:long, intervalMin:double, intervalMax:double) : void + + {static} NormSetGrttProbingTOS(sessionHandle:long, probeTOS:byte) : void + + {static} NormSetBackoffFactor(sessionHandle:long, backoffFactor:double) : void + + {static} NormSetGroupSize(sessionHandle:long, groupSize:uint) : void + + {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void + + {static} NormFileEnqueue(sessionHandle:long, fileName:string): long + + {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long + + {static} NormDataEnqueue(sessionHandle:long, dataPtr:string, dataLen:int) : long + + {static} NormDataEnqueue(sessionHandle:long, dataPtr:string, dataLen:int, infoPtr:string, infoLen:int) : long + + {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool + + {static} NormStreamOpen(sessionHandle:long, bufferSize:long) : long + + {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long + + {static} NormStreamClose(streamHandle:long) : void + + {static} NormStreamClose(streamHandle:long, graceful:bool) : void + + {static} NormGetStreamBufferSegmentCount(bufferBytes:uint, segmentSize:ushort, blockSize:ushort) : uint + + {static} NormStreamWrite(streamHandle:long, buffer:string, numBytes:uint) : uint + + {static} NormStreamFlush(streamHandle:long) : void + + {static} NormStreamFlush(streamHandle:long, eom:bool, flushMode:NormFlushMode) : void + + {static} NormStreamSetAutoFlush(streamHandle:long, flushMode: NormFlushMode) : void + + {static} NormStreamSetPushEnable(streamHandle:long, pushEnable:bool) : void + + {static} NormStreamHasVacancy(streamHandle:long) : bool + + {static} NormStreamGetVacancy(streamHandle:long, bytesWanted:uint) : uint + + {static} NormStreamMarkEom(streamHandle:long) : void + + {static} NormSetWatermark(sessionHandle:long, objectHandle:long) : bool + + {static} NormSetWatermark(sessionHandle:long, objectHandle:long, overrideFlush:bool) : bool + + {static} NormSetWatermarkEx(sessionHandle:long, objectHandle:long, buffer:string, numBytes:uint) : bool + + {static} NormSetWatermarkEx(sessionHandle:long, objectHandle:long, buffer:string, numBytes:uint, overrideFlush:bool) : bool + + {static} NormResetWatermark(sessionHandle:long) : bool + + {static} NormCancelWatermark(sessionHandle:long) : void + + {static} NormAddAckingNode(sessionHandle:long, nodeId:long) : bool + + {static} NormRemoveAckingNode(sessionHandle:long, nodeId:long) : void + + {static} NormGetAckingNodeHandle(sessionHandle:long, nodeId:long) : long + + {static} NormSetAutoAckingNodes(sessionHandle:long, trackingStatus:NormTrackingStatus) : void + + {static} NormGetAckingStatus(sessionHandle:long) : NormAckingStatus + + {static} NormGetAckingStatus(sessionHandle:long, nodeId:long) : NormAckingStatus + + {static} NormGetNextAckingNode(sessionHandle:long, nodeId:long) : bool + + {static} NormGetNextAckingNode(sessionHandle:long, nodeId:long, ackingStatus:NormAckingStatus) : bool + + {static} NormGetAckEx(sessionHandle:long, nodeId:long, buffer:string, buflen:uint) : bool + + {static} NormSendCommand(sessionHandle:long, cmdBuffer:string, cmdLength:uint) : bool + + {static} NormSendCommand(sessionHandle:long, cmdBuffer:string, cmdLength:uint, robust:bool) : bool + + {static} NormCancelCommand(sessionHandle:long) : void + + {static} NormSetSynStatus(sessionHandle:long, state:bool) : void + + {static} NormStartReceiver(sessionHandle:long, bufferSpace:long) : bool + + {static} NormStopReceiver(sessionHandle:long) : void + + {static} NormSetRxCacheLimit(sessionHandle:long, countMax:ushort) : void + + {static} NormSetRxSocketBuffer(sessionHandle:long, bufferSize:uint) : bool + + {static} NormSetSilentReceiver(sessionHandle:long, silent:bool) : void + + {static} NormSetSilentReceiver(sessionHandle:long, silent:bool, maxDelay:int) : void + + {static} NormSetDefaultUnicastNack(sessionHandle:long, unicastNacks:bool) : void + + {static} NormNodeSetUnicastNack(remoteSender:long, unicastNacks:bool) : void + + {static} NormSetDefaultSyncPolicy(sessionHandle:long, syncPolicy:NormSyncPolicy) : void + + {static} NormSetDefaultNackingMode(sessionHandle:long, nackingMode:NormNackingMode) : void + + {static} NormNodeSetNackingMode(remoteSender:long, nackingMode:NormNackingMode) : void + + {static} NormObjectSetNackingMode(objectHandle:long, nackingMode:NormNackingMode) : void + + {static} NormSetDefaultRepairBoundary(sessionHandle:long, repairBoundary:NormRepairBoundary) : void + + {static} NormNodeSetRepairBoundary(remoteSender:long, repairBoundary:NormRepairBoundary) : void + + {static} NormSetDefaultRxRobustFactor(sessionHandle:long, robustFactor:int) : void + + {static} NormNodeSetRxRobustFactor(remoteSenderremoteSender, robustFactor:int) : void + + {static} NormPreallocateRemoteSender(sessionHandle:long, bufferSize:ulong, segmentSize:ushort, numData:ushort, numParity:ushort) : bool + + {static} NormPreallocateRemoteSender(sessionHandle:long, bufferSize:ulong, segmentSize:ushort, numData:ushort, numParity:ushort, streamBufferSize:uint) : bool + + {static} NormStreamRead(streamHandle:long, buffer:string, numBytes:uint) : bool + + {static} NormStreamSeekMsgStart(streamHandle:long) : bool + + {static} NormStreamGetReadOffset(streamHandle:long) : uint + + {static} NormStreamGetBufferUsage(streamHandle:long) : uint + + {static} NormObjectGetType(objectHandle:long) : NormObjectType + + {static} NormObjectHasInfo(objectHandle:long) : bool + + {static} NormObjectGetInfoLength(objectHandle:long) : ushort + + {static} NormObjectGetInfo(objectHandle:long, buffer:char, bufferLen:ushort) : ushort + + {static} NormObjectGetSize(objectHandle:long) : int + + {static} NormObjectGetBytesPending(objectHandle:long) : int + + {static} NormObjectCancel(objectHandle:long) : void + + {static} NormObjectRetain(objectHandle:long) : void + + {static} NormObjectRelease(objectHandle:long) : void + + {static} NormFileGetName(fileHandle:long, nameBuffer:string, bufferLen:int) : bool + + {static} NormFileRename(fileHandle:long, fileName:string) : bool + + {static} NormDataAccessData(objectHandle:long) : string + + {static} NormDataDetachData(objectHandle:long) : string + + {static} NormObjectGetSender(objectHandle:long) : long + + {static} NormNodeGetId(nodeHandle:long) : uint + + {static} NormNodeGetAddress(nodeHandle:long, addrBuffer:string, bufferLen: uint) : bool + + {static} NormNodeGetAddress(nodeHandle:long, addrBuffer:string, bufferLen: uint, port:ushort) : bool + + {static} NormNodeGetGrtt(remoteSender:long) : double + + {static} NormNodeGetCommand(remoteSender:long, :string, buflen:uint) : bool + + {static} NormNodeSendAckEx(remoteSender:long, buffer:string, numBytes:uint) : bool + + {static} NormNodeGetWatermarkEx(remoteSender:long, buffer:string, buflen:uint) : bool + + {static} NormNodeFreeBuffers(remoteSender:long) : void + + {static} NormNodeDelete(remoteSender:long) : void + + {static} NormNodeRetain(nodeHandle:long) : void + + {static} NormNodeRelease(nodeHandle:long) : void + + {static} NormReleasePreviousEvent(instanceHandle:long) : void + + {static} NormCountCompletedObjects(sessionHandle:long) : uint + + {static} NormNodeSetAutoDelete(remoteSender:long, autoDelete:bool) : void + + {static} NormNodeAllowSender(senderId:long) : bool + + {static} NormNodeDenySender(senderId:long) : bool +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml new file mode 100644 index 00000000..658ec091 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml @@ -0,0 +1,7 @@ +@startuml +class NormData { + ~ NormData(handle:long) + + byte[] Data <> +} +NormObject <|-- NormData +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml new file mode 100644 index 00000000..3a1a991a --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml @@ -0,0 +1,14 @@ +@startuml +class NormEvent { + - _type: NormEventType + - _sessionHandle: long + - _nodeHandle: long + - _objectHandle: long + + NormEvent(type:NormEventType, sessionHandle:long, nodeHandle:long, objectHandle:long) + + Type : NormEventType <> + + Session : NormSession <> + + Node : NormNode <> + + Object : NormObject <> + + <> ToString() : string +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml new file mode 100644 index 00000000..49e3c20f --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml @@ -0,0 +1,8 @@ +@startuml +class NormFile { + ~ NormFile(handle:long) + + Name : string <> + + Rename(filename:string) : void +} +NormObject <|-- NormFile +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml new file mode 100644 index 00000000..7aa9e8b3 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml @@ -0,0 +1,21 @@ +@startuml +class NormInstance { + - _handle: long + + NormInstance() + + NormInstance(priorityBoost:bool) + - CreateInstance(priorityBoost:bool) : void + + DestroyInstance() : void + + StopInstance() : void + + RestartInstance() : bool + + SuspendInstance() : bool + + ResumeInstance() : void + + SetCacheDirectory(cachePath:string) : void + + OpenDebugLog(filename:string) : void + + CloseDebugLog() : void + + OpenDebugPipe(pipename:string) : void + + DebugLevel: int <> <> + + HasNextEvent(sec:int, usec:int) : boolean + + GetNextEvent() : NormEvent + + CreateSession(address:string, port:int, localNodeId:long) : NormSession +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml new file mode 100644 index 00000000..d9757b9a --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml @@ -0,0 +1,18 @@ +@startuml +class NormNode { + + <> NORM_NODE_ANY : long = 0xffffffff + - _handle : long + ~ NormNode(handle:long) + + SetUnicastNack(state:bool) : void + + SetNackingMode(nackingMode:NormNackingMode) : void + + SetRepairBoundary(repairBoundary:NormRepairBoundary) : void + + SetRxRobustFactor(robustFactor:int) : void + + Id : long <> + + Address : SocketAddress <> + + Grtt : double <> + + GetCommand(buffer:byte[], offset:int, length:int) : int + + FreeBuffers() : void + + Retain() : void + + Release(): void +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml new file mode 100644 index 00000000..2a48d175 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml @@ -0,0 +1,17 @@ +@startuml +class NormObject { + # _handle:long + ~ NormObject(handle:long) + + SetNackingMode(nackingMode:NormNackingMode) : void + + Type : NormObjectType <> + + Info : byte[] <> + + Size : long <> + + GetBytesPending() : long + + Cancel() : void + + Retain() : void + + Release() : void + + Sender : NormNode <> + + <> GetHashCode() : int + + <> Equals(obj:object) : bool +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml new file mode 100644 index 00000000..e1fd0647 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml @@ -0,0 +1,70 @@ +@startuml +class NormSession { + {static} -_normSessions : Dictionary + - _handle : long + ~ NormSession(handle:long) + {static} ~ NormSession GetSession(handle:long) + + DestroySession() : void + - DestroySessionNative() : void + + LocalNodeId: long <> + + SetTxPort(port:int) : void + + SetTxPort(port:int, enableReuse:bool, txAddress:string) : void + + SetRxPortReuse(enable:bool) : void + + SetRxPortReuse(enable:bool, rxBindAddress:string, senderAddress:string, senderPort:int) : void + + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool) : void + + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool, tolerateLoss:bool) : void + + SetMulticastInterface(interfaceName:string) : void + + SetSSM(sourceAddr:string) : void + + SetTTL(ttl:byte) : void + + SetTOS(tos:byte) : void + + SetLoopback(loopbackEnable:bool) : void + + SetMessageTrace(flag:bool) : void + + SetTxLoss(precent:double) : void + + SetRxLoss(precent:double) : void + + ReportInterval: double <> <> + + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void + + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void + + StopSender() : void + + SetTxOnly(txOnly:bool) : void + + TxRate:double <> <> + + SetFlowControl(flowControlFactor:double) : void + + SetTxSocketBuffer(bufferSize:long) : void + + SetCongestionControl(enable:bool) : void + + SetCongestionControl(enable:bool, adjustRate:bool) : void + + SetTxRateBounds(rateMin:double, rateMax:double) : void + + SetTxCacheBounds(sizeMax:long, countMin:long, countMax:long) : void + + SetAutoParity(autoParity:short) : void + + GrttEstimate:double <> <> + + SetGrttMax(grttMax:double) : void + + SetGrttProbingMode(probingMode:NormProbingMode) : void + + SetGrttProbingInterval(intervalMin:double, intervalMax:double) : void + + SetBackoffFactor(backoffFactor:double) : void + + SetGroupSize(groupSize:long) : void + + SetTxRobustFactor(robustFactor:int) : void + + FileEnqueue(filename:string) : NormFile + + FileEnqueue(filename:string, info:byte[], infoOffset:int, infoLength:int) : NormFile + + DataEnqueue(dataBuffer:MemoryStream, dataOffset:int, dataLength:int) : NormData + + DataEnqueue(dataBuffer:MemoryStream, dataOffset:int, dataLength:int, info:byte[], infoOffset:int, infoLength:int) : NormData + + RequeueObject(object:NormObject) : void + + StreamOpen(bufferSize:long) : NormStream + + StreamOpen(bufferSize:long, info:byte[], infoOffset:int, infoLength:int) : NormStream + + SetWatermark(object:NormObject, overrideFlush:bool) : void + + CancelWatermark() : void + + ResetWatermark() : void + + AddAckingNode(nodeId:long) : void + + RemoveAckingNode(nodeId:long) : void + + GetAckingStatus(nodeId:long): NormAckingStatus + + SendCommand(cmdBuffer:byte[], cmdOffset:int, cmdLength:int, robust:bool) : void + + CancelCommand() : void + + StartReceiver(bufferSpace:long) : void + + StopReceiver() : void + + SetRxCacheLimit(countMax:int) : void + + SetRxSocketBuffer(bufferSize:long) : void + + SetSilentReceiver(silent:bool, maxDelay:int) : void + + SetDefaultUnicastNack(enabled:bool) : void + + SetDefaultSyncPolicy(syncPolicy:NormSyncPolicy) : void + + SetDefaultNackingMode(nackingMode: NormNackingMode) : void + + SetDefaultRepairBoundary(repairBoundary: NormRepairBoundry) : void + + SetDefaultRxRobustFactor(robustFactor: int): void +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml new file mode 100644 index 00000000..e4c1831a --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml @@ -0,0 +1,17 @@ +@startuml +class NormStream { + ~ NormStream(handle:long) + + Close() : void + + Close(boolean graceful) : void + + Write(buffer:byte[], offset:int, length:int) : int + + Flush() : void + + Flush(eom:bool, flushMode:NormFlushMode) : void + + SetAutoFlush(flushMode:NormFlushMode) : void + + SetPushEnable(pushEnable:bool) : void + + HasVacancy : bool <> + + MarkEom() : void + + Read(buffer:byte[], offset:int, length:int) : int + + SeekMsgStart() : bool + + ReadOffset : long <> +} +@enduml \ No newline at end of file diff --git a/src/dotnet/design/SequenceDiagrams/NormFileSend.puml b/src/dotnet/design/SequenceDiagrams/NormFileSend.puml new file mode 100644 index 00000000..8f1e7630 --- /dev/null +++ b/src/dotnet/design/SequenceDiagrams/NormFileSend.puml @@ -0,0 +1,49 @@ +@startuml NormFileSend +Client -> NormInstance : new() +NormInstance -> NormInstance : CreateInstance(false) +NormInstance -> NormApi : NormCreateInstance(false) +NormInstance <- NormApi : instanceHandle +Client <- NormInstance : NormInstance +Client -> NormInstance : CreateSession(address, port, localNodeId) +NormInstance -> NormApi : NormCreateSession(instanceHandle, address, port, localNodeId) +NormInstance <- NormApi : sessionHandle +alt sessionHandle == NORM_SESSION_INVALID + NormInstance -> IOException : throw new("Failed to create session") +end +NormInstance -> NormSession : new(sessionHandle) +NormInstance <- NormSession : NormSession +Client <- NormInstance : NormSession +Client -> NormSession : StartSender(sessionId, bufferSpace, segmentSize, blockSize, numParity, fecId) +NormSession -> NormApi : NormStartSender(sessionHandle, sessionId, bufferSpace, segmentSize, blockSize, numParity, fecId) +NormSession <- NormApi : success +alt success == false + NormSession -> IOException : throw new("Failed to start sender") +end +Client -> NormSession : FileEnqueue(filename) +NormSession -> ASCII : GetBytes(filename) +NormSession <- ASCII : info +NormSession -> NormApi : NormFileEnqueue(sessionHandle, filename, info, info.Length) +NormSession <- NormApi : objectHandle +alt objectHandle == NormApi.NORM_OBJECT_INVALID + NormSession -> IOException : throw new("Failed to enqueue file") +end +NormSession -> NormFile: new(objectHandle) +NormSession <- NormFile : NormFile +Client <- NormSession : NormFile +loop NormEvent != null + Client -> NormInstance : GetNextEvent() + NormInstance -> NormApi : NormGetNextEvent(instanceHandle, NormEvent) + NormInstance <- NormApi : success + alt success == false + Client <- NormInstance : null + end + Client <- NormInstance : NormEvent +end +Client -> NormSession : StopSender(); +NormSession -> NormApi : NormStopSender(sessionHandle) +Client -> NormSession : DestroySession() +NormSession -> NormSession : DestroySessionNative() +NormSession -> NormApi : NormDestroySession(sessionHandle) +Client -> NormInstance : DestroyInstance() +NormInstance -> NormApi : NormDestroyInstance(instanceHandle) +@enduml \ No newline at end of file From 26944463e22b902e0c7a64e0f72c53f388cf5b27 Mon Sep 17 00:00:00 2001 From: mullerj Date: Wed, 1 Mar 2023 17:05:29 -0500 Subject: [PATCH 02/29] .NET Extension Design Implementation (#10) * changes * Removed GetsNextEvent test * Implemented GetNextEvent * Fixed NormStream design * Fixed HasNextEvent design * Added NormGetNextEvent overload * Implemented HasNextEvent * Updated design * Added Kernel32 to design * Modified to use .NET EventWaitHandle instead of kernel32 P/Invoke * Modified to only copy norm.dll on Windows * Added copy for non Windows * add start and stop receiver * add receiver * Modified non-Windows to use Socket for HasNextEvent * Deleted NormFileTests since they break the other tests * Fixed ReceiverReceives test * Updated ReceiverReceives test name * Modified ReceivesFile to get all events instead of adding to array * Cleaned up NormInstance formatting * Refactored tests * Updated NormFile * Implemented send stream * Implemented receive stream * Implemented NormFile Name * Updated ReceivesFile to use events * data enqueue * Fixed merge * Fixed ReceivesData test * Modified data test to compare collections without order * stop instance working * enqueues data * Reverted upgrade to .NET 7 * Restartinstance is working * Modified NormData to read bytes instead of as string * Removed using statements in NormData * Updated design * Updated design * Switched to nint * Switched design to nint * unfinished test for suspendinstance * working suspend instance * added resume function to instance * Updated session send and receive data tests * Removed launch settings * session configuration functions * Modified to use random port due to intermittent failed tests due to session conflict * Modified to randomize text content * Implemented NormNode Address * Updated design for NormNode * Implemented NormNode Grtt * Implemented NormNode Id * implement session configuration functions * instance * missing return types * add missing handle param * normNode * Implemented RequeueObject * Implemented SetWatermark and AckingNode * Implemented CancelWatermark * Implemented ResetWatermark * Implemented SendCommand * Implemented CancelCommand * Implemented DebugLevel. Removed DebugPipe * Updated SetTxPort * Updated SetRxPortReuse * Implemented SetEcnSupport * Added test for RxLoss * Implemented ReportInterval * Refactored session calculated properties * Updated enqueue methods * Updated StreamOpen design * Fixed SetWatermark overload * Implemented GetAckingStatus * Removed SendCommand overload * Removed SetSilentReceiver overload * Implemented NormEvent ToString * Implemented NormFile Rename * Implemented SetUnicastNack * Added ReceivesCommand test * Implemented SetNackingMode * Implemented SetRepairBoundary * Added Fact to SetsRepairBoundary test * Implemented SetRxRobustFactor * Implemented NormNode Command * Implemented FreeBuffers * Implemented Retain and Release * Fix using namespaces * test get object type * NormObject get size * NormObject setNackingMode * NormObject get bytes pending * NormObjet cancel * NormObject retain * NormObject release * NormObject get sender * NormObject get hashcode * normObject equals * Implemented NormObject Equals * Removed redundant cast * Removed unused using directive * Removed static using * Added NormApi to global Usings * Added Enums to global usings * Cleaned up NormObject Equals * Cleaned up NormObject GetHashCode() test * Cleaned up warning * normStream has vacancy * normStream read offset * normStream seek msg start * NormEventListener interface * normStream set push enable * normStream set auto flush * Simplified NormApi usage * Updated design of NormStream * public interface * Fixed INormEventListener * fixing name space * commit initializing inputstream * Added StreamBreakException * Fixed formatting * Fixed formatting * Fixed formatting * Initial implementation of NormOutputStream * Cleaned up NormObject Equals * NormInputStream class Co-authored-by: mullerj * normStream data offset * move offset length arithmetic into write method * building normInputstream * all method implemented Co-authored-by: mullerj * NormStreamRead * Fixed build error * Completed ReceivesStreamWithOffset test * Added retries to GetsCommand test * Updated type for NormStreamRead * Simplified NormStream Read * Simplified NormStream Write * Added dataOffset to DataEnqueue * Fixed DataEnqueue overload * Added infoOffset to FileEnqueue * Added infoOffset to StreamOpen * Updated design * Removed extra overload of FileEnqueue * Removed DataEnqueue overloads * Removed extra overload from StreamOpen * Added HasNextEvent overload * Implemented OpenDebugPipe * add thread locking * locking for properties * Not supported exceptions * Implementing sychronize * fixing inputstream * adding sychronize message * Reorganized NormApi and updated design * Modified to skip GetsCommand test on IO exception * WIP: 537b706 adding sychronize message * adding message to expections * Fixed test file cleanup * Added locking to NormSession methods that use static field * Changed StartsSender to skippablefact * Changed to SetsNackingMode to skippable fact * Converted tests to skippable * Converted more tests to skippable * Converted all tests that start sender to skippable * Updated README about skipped tests * documentation * 2 doc * Cleaned up input and output stream classes * Cleaned up NormApi * Updated NormOutputStream Write * Added document comments to NormApi * Added comments to NormApi * Removed unnecessary usings * Added comments to NormApi * set up the rest of doc * api documentation * fix typo * fix spacing * Doc for instance need clean up * clean up * document norm session * NormStream documentation * commit for node * NormObject docs * Updated NormApi comments * Cleaned up NormInstance comments * Cleaned up NormNode comments * Cleaned up NormObject comments * Cleaned up NormSession comments * Cleaned up NormStream comments * Cleaned up NormObject comments * Adding commits * Update get commit * clean up on data and event * Norm Data change * clean up normfile docs * clean up normstream * clean up api docs * Fixed data * space approved * Cleaned up NormData comment * documentation clean up * Update the ReadMe * Cleaned up comments * Added comments to NormAckingStatus * Added comments to NormEventType * Added comments to NormFecType * Added comments to NormNackingMode * Added comments to NormObjectType * Added comments to NormProbingMode * Added comments to NormRepairBoundary * Added comments to NormFlushMode * Added comments to NormSyncPolicy * Cleaned up comments. Fixed default for StartSender * Added comments to constants * Moved constants to relevant classes * Updated design * Removed NormDataDetachData since it is not referenced * Updated design * Updated input and output stream design * Updated packaging README * Removed language version since redundant * Cleaned up project file * Updated to package from the dotnet directory (test project is set to not pack) * Added version to project --------- Co-authored-by: syermak Co-authored-by: Sylvester Freeman III Co-authored-by: Sergiy <47069243+SergiyYermak@users.noreply.github.com> Co-authored-by: mullerj --- .gitignore | 276 +++ README-DotNet.md | 54 + src/dotnet/Mil.Navy.Nrl.Norm.sln | 39 + .../Navy/Nrl/Norm/Enums/NormNackingMode.puml | 2 +- .../Mil/Navy/Nrl/Norm/IO/NormInputStream.puml | 9 + .../Navy/Nrl/Norm/IO/NormOutputStream.puml | 13 +- .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 109 +- .../design/Mil/Navy/Nrl/Norm/NormFile.puml | 1 + .../Mil/Navy/Nrl/Norm/NormInstance.puml | 5 +- .../design/Mil/Navy/Nrl/Norm/NormNode.puml | 6 +- .../design/Mil/Navy/Nrl/Norm/NormObject.puml | 1 + .../design/Mil/Navy/Nrl/Norm/NormSession.puml | 10 +- .../design/Mil/Navy/Nrl/Norm/NormStream.puml | 1 + .../design/SequenceDiagrams/NormFileSend.puml | 8 +- .../Enums/NormAckingStatus.cs | 28 + .../Mil.Navy.Nrl.Norm/Enums/NormEventType.cs | 194 ++ .../Mil.Navy.Nrl.Norm/Enums/NormFecType.cs | 21 + .../Mil.Navy.Nrl.Norm/Enums/NormFlushMode.cs | 22 + .../Enums/NormNackingMode.cs | 21 + .../Mil.Navy.Nrl.Norm/Enums/NormObjectType.cs | 25 + .../Enums/NormProbingMode.cs | 24 + .../Enums/NormRepairBoundary.cs | 22 + .../Mil.Navy.Nrl.Norm/Enums/NormSyncPolicy.cs | 22 + .../IO/INormEventListener.cs | 7 + .../Mil.Navy.Nrl.Norm/IO/NormInputStream.cs | 332 +++ .../Mil.Navy.Nrl.Norm/IO/NormOutputStream.cs | 392 +++ .../IO/StreamBreakException.cs | 9 + .../Mil.Navy.Nrl.Norm.csproj | 10 + src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 1133 +++++++++ src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs | 39 + src/dotnet/src/Mil.Navy.Nrl.Norm/NormEvent.cs | 112 + src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs | 54 + .../src/Mil.Navy.Nrl.Norm/NormInstance.cs | 236 ++ src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs | 147 ++ .../src/Mil.Navy.Nrl.Norm/NormObject.cs | 163 ++ .../src/Mil.Navy.Nrl.Norm/NormSession.cs | 923 +++++++ .../src/Mil.Navy.Nrl.Norm/NormStream.cs | 184 ++ src/dotnet/src/Mil.Navy.Nrl.Norm/Usings.cs | 3 + .../Mil.Navy.Nrl.Norm.IntegrationTests.csproj | 34 + .../NormInstanceTests.cs | 261 ++ .../NormSessionTests.cs | 2139 +++++++++++++++++ .../Usings.cs | 1 + 42 files changed, 6996 insertions(+), 96 deletions(-) create mode 100644 README-DotNet.md create mode 100644 src/dotnet/Mil.Navy.Nrl.Norm.sln create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormAckingStatus.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormEventType.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFecType.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFlushMode.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormNackingMode.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormObjectType.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormProbingMode.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormRepairBoundary.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormSyncPolicy.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/IO/INormEventListener.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormInputStream.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormOutputStream.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/IO/StreamBreakException.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormEvent.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Usings.cs create mode 100644 src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Mil.Navy.Nrl.Norm.IntegrationTests.csproj create mode 100644 src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs create mode 100644 src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs create mode 100644 src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Usings.cs diff --git a/.gitignore b/.gitignore index 7faead0e..9b5994ff 100755 --- a/.gitignore +++ b/.gitignore @@ -18,9 +18,16 @@ norp/makefiles/norp *.o *.a +# build java files +*.class + +#build android files +android/.gradle + # waf cruft .waf* .lock-waf* +waf*/* # OS generated files # .DS_Store @@ -30,3 +37,272 @@ norp/makefiles/norp .Trashes ehthumbs.db Thumbs.db + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml +*.sqlite +*.sqlite-shm +*.sqlite-wal + +# Nuget files +DeploymentSettings.props +Directory.Build.props +Directory.Build.targets +Packages.props + +#Local Log Files +log*.txt diff --git a/README-DotNet.md b/README-DotNet.md new file mode 100644 index 00000000..d288280e --- /dev/null +++ b/README-DotNet.md @@ -0,0 +1,54 @@ +.NET extension for NORM +========================== + +By: + Jeff Muller + Sylvester Freeman + Sergiy Yermak + +The .NET extension for NORM provides a .NET wrapper for the NORM C API. + +For documentation about the main NORM API calls, refer to the NORM Developers +guide in the regular NORM distribution. + +The .NET extension can be built using the .NET CLI. + +------------ +Requirements +------------ + +The .NET extension for NORM requires at least .NET SDK 6.0. + +The NORM library should be built prior to building the .NET extension since it is invoked by the .NET extension. + +------------ +Building +------------ + +To build the .NET extension for NORM, execute the .NET CLI command in the src/dotnet directory: + + ``` + dotnet build . + ``` + +------------ +Testing +------------ + +To test the .NET extension for NORM, execute the .NET CLI command in the src/dotnet directory: + + ``` + dotnet test . + ``` + +The test command results should show that all tests have passed. +Some tests might be skipped due to IO exception. + +------------ +Packaging +------------ +To package the .NET extension for NORM, execute the .NET CLI command in the src/dotnet directory: + +``` +dotnet pack . -c Release +``` diff --git a/src/dotnet/Mil.Navy.Nrl.Norm.sln b/src/dotnet/Mil.Navy.Nrl.Norm.sln new file mode 100644 index 00000000..d51cd5d6 --- /dev/null +++ b/src/dotnet/Mil.Navy.Nrl.Norm.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33213.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mil.Navy.Nrl.Norm", "src\Mil.Navy.Nrl.Norm\Mil.Navy.Nrl.Norm.csproj", "{0B38FE25-685E-4ADC-B63D-CA43EC023E1F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A6F15F54-6953-49E7-9394-8F6BF6EF64D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mil.Navy.Nrl.Norm.IntegrationTests", "tests\Mil.Navy.Nrl.Norm.IntegrationTests\Mil.Navy.Nrl.Norm.IntegrationTests.csproj", "{E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8B6C912C-2A15-41F3-B3A4-E9C01E6672DE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F} = {A6F15F54-6953-49E7-9394-8F6BF6EF64D0} + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49} = {8B6C912C-2A15-41F3-B3A4-E9C01E6672DE} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8BC1F27F-D633-4B45-A16D-D112CB08840E} + EndGlobalSection +EndGlobal diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml index 41f021ac..357ea0b2 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml @@ -1,6 +1,6 @@ @startuml enum NormNackingMode { - ORM_NACK_NONE + NORM_NACK_NONE NORM_NACK_INFO_ONLY NORM_NACK_NORMAL } diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml index dc3434d7..c6cfef6a 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml @@ -28,6 +28,15 @@ class NormInputStream { + Read() : int + <> Read(buffer:byte[], offset:int, length:int) : int - ProcessEvent() : void + + <> Flush() : void + + <> Write(buffer:byte[], offset:int, count:int) : void + + <> Seek(offset:long, origin:SeekOrigin) : long + + <> SetLength(value:long) : void + + <> CanRead : bool <> + + <> CanSeek : bool <> + + <> CanWrite : bool <> + + <> Length : long <> + + <> Position : long <> <> } Stream <|-- NormInputStream @enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml index 9dffa7b0..72468b11 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml @@ -17,7 +17,7 @@ class NormOutputStream { + SetTtl(ttl:byte) : void + SetTos(tos:byte) : void + SetCongestionControl(ccEnabled:bool, ccAdjustRate: bool) : void - + SetTxRateBounds(rateMin:double, rateMax:double) : void + + SetTxRateBounds(minTxRate:double, maxTxRate:double) : void + TxRate:double <> <> + GrttEstimate:double <> <> + SetGroupSize(groupSize:long) : void @@ -30,11 +30,20 @@ class NormOutputStream { + RemoveNormEventListener(normEventListener:INormEventListener): void - FireNormEventOccured(normEvent:NormEvent) : void + Open(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, repairWindow:long) : void - + <> Dispose() : void + + <> Close() : void + IsClosed : bool <> + Write(b:int) : void + <> Write(buffer:byte[], offset:int, count:int) - ProcessEvent() : void + + <> Flush() : void + + <> Read(buffer:byte[], offset:int, length:int) : int + + <> Seek(offset:long, origin:SeekOrigin) : long + + <> SetLength(value:long) : void + + <> CanRead : bool <> + + <> CanSeek : bool <> + + <> CanWrite : bool <> + + <> Length : long <> + + <> Position : long <> <> } Stream <|-- NormOutputStream @enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index d6ac2674..c8a210c1 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -1,11 +1,14 @@ @startuml +struct NormEvent +{ + + Type : NormEventType + + Session : long + + Sender : long + + Object : long +} class NormApi { - + <> NORM_OBJECT_INVALID : int = 0 - + <> FILENAME_MAX : int = 260 + <> NORM_LIBRARY : string = "norm" - + {static} NormGetVersion(major:int, minor:int, patch:int) : int - + {static} NormCreateInstance() : long + {static} NormCreateInstance(priorityBoost:bool) : long + {static} NormDestroyInstance(instanceHandle:long) : void + {static} NormStopInstance(instanceHandle:long) : void @@ -13,63 +16,36 @@ class NormApi + {static} NormSuspendInstance(instanceHandle:long) : bool + {static} NormResumeInstance(instanceHandle:long) : void + {static} NormSetCacheDirectory(instanceHandle:long, cachePath:string) : bool - + {static} NormGetNextEvent(instanceHandle:long, theEvent:NormEvent) : bool + {static} NormGetNextEvent(instanceHandle:long, theEvent:NormEvent, waitForEvent:bool) : bool - + {static} NormGetDescriptor(instanceHandle:long) : NormDescriptor + + {static} NormGetDescriptor(instanceHandle:long) : int + {static} NormCreateSession(instanceHandle:long, sessionAddress:string, sessionPort:int, localNodeId:long) : long + {static} NormDestroySession(sessionHandle:long) : void - + {static} NormGetInstance(sessionHandle:long) : long - + {static} NormIsUnicastAddress(address:string) : bool - + {static} NormSetUserTimer(sessionHandle:long, seconds:double) : void - + {static} NormCancelUserTimer(sessionHandle:long) : void + {static} NormGetLocalNodeId(sessionHandle:long) : long - + {static} NormGetAddress(sessionHandle:long, addrBuffer:string, bufferLen:uint) : bool - + {static} NormGetAddress(sessionHandle:long, addrBuffer:string, bufferLen:uint, port:ushort) : bool - + {static} NormGetRxPort(sessionHandle:long) : ushort - + {static} NormSetTxPort(sessionHandle:long, txPortNumber:ushort) : bool - + {static} NormSetTxPort(sessionHandle:long, txPortNumber:ushort, enableReuse:bool, txBindAddress:string) : bool - + {static} NormGetTxPort(sessionHandle:long) : ushort - + {static} NormSetTxOnly(sessionHandle:long, txOnly:bool) : void + + {static} NormSetTxPort(sessionHandle:long, txPortNumber:int, enableReuse:bool, txBindAddress:string) : bool + {static} NormSetTxOnly(sessionHandle:long, txOnly:bool, connectToSessionAddress:bool) : void - + {static} NormLimitObjectInfo(sessionHandle:long, state:bool) : void - + {static} NormPresetObjectInfo(sessionHandle:long, objectSize:ulong, segmentSize:ushort, numData:ushort, numParity:ushort) : bool - + {static} NormSetId(sessionHandle:long, normId:long) : void - + {static} NormChangeDestination(sessionHandle:long, sessionAddress:string, sessionPort:ushort) : bool - + {static} NormChangeDestination(sessionHandle:long, sessionAddress:string, sessionPort:ushort, connectToSessionAddress:bool) : bool - + {static} NormSetServerListner(sessionHandle:long, state:bool) : void - + {static} NormTransferSender(sessionHandle:long, sender:long) : bool - + {static} NormSetRxPortReuse(sessionHandle:long, enableReuse:bool) : void - + {static} NormSetRxPortReuse(sessionHandle:long, enableReuse:bool, rxBindAddress:string, senderAddress:string, senderPort:ushort) : void - + {static} NormGetRxBindAddress(sessionHandle:long, addr:string, addrLen:uint, port:ushort) : bool - + {static} NormSetEcnSupport(sessionHandle:long, ecnEnable:bool) : void + + {static} NormSetRxPortReuse(sessionHandle:long, enableReuse:bool, rxBindAddress:string, senderAddress:string, senderPort:int) : void + {static} NormSetEcnSupport(sessionHandle:long, ecnEnable:bool, ignoreLoss:bool, tolerateLoss:bool) : void + {static} NormSetMulticastInterface(sessionHandle:long, interfaceName:string) : bool + {static} NormSetSSM(sessionHandle:long, sourceAddress:string) : bool + {static} NormSetTTL(sessionHandle:long, ttl:byte) : bool + {static} NormSetTOS(sessionHandle:long, tos:byte) : bool + {static} NormSetLoopback(sessionHandle:long, loopback:bool) : bool - + {static} NormSetMulticastLoopback(sessionHandle:long, loopback:bool) : bool - + {static} NormSetFragmentation(sessionHandle:long, fragmentation:bool) : bool + {static} NormSetMessageTrace(sessionHandle:long, state:bool) : void + {static} NormSetTxLoss(sessionHandle:long, precent:double) : void + {static} NormSetRxLoss(sessionHandle:long, precent:double) : void + {static} NormOpenDebugLog(instanceHandle:long, path:string) : bool + {static} NormCloseDebugLog(instanceHandle:long) : void + {static} NormOpenDebugPipe(instanceHandle:long, pipeName:string) : bool - + {static} NormCloseDebugPipe(instanceHandle:long) : void + {static} NormSetDebugLevel(level:int) : void + {static} NormGetDebugLevel() : int + {static} NormSetReportInterval(sessionHandle:long, interval:double) : void - + {static} NormGetReportInterval(sessionHandle:long) : double + {static} NormGetRandomSessionId() : int - + {static} NormStartSender(sessionHandle:long, instanceId:int, bufferSpace:long, segmentSize:int, numData:short, numParity:short) : bool + {static} NormStartSender(sessionHandle:long, instanceId:int, bufferSpace:long, segmentSize:int, numData:short, numParity:short, fecId:NormFecType) : bool + {static} NormStopSender(sessionHandle:long) : void + {static} NormSetTxRate(sessionHandle:long, bitsPerSecond:double) : void + {static} NormGetTxRate(sessionHandle:long) : double - + {static} NormSetTxSocketBuffer(sessionHandle:long, bufferSize:uint) : bool + + {static} NormSetTxSocketBuffer(sessionHandle:long, bufferSize:long) : bool + {static} NormSetFlowControl(sessionHandle:long, flowControlFactor:double) : void - + {static} NormSetCongestionControl(sessionHandle:long, enable:bool) : void + {static} NormSetCongestionControl(sessionHandle:long, enable:bool, adjustRate:bool) : void + {static} NormSetTxRateBounds(sessionHandle:long, rateMin:double, rateMax:double) : void + {static} NormSetTxCacheBounds(sessionHandle:long, sizeMax:long, countMin:long, countMax:long) : void @@ -79,52 +55,32 @@ class NormApi + {static} NormSetGrttMax(sessionHandle:long, grttMax:double) : void + {static} NormSetGrttProbingMode(sessionHandle:long, probingMode:NormProbingMode) : void + {static} NormSetGrttProbingInterval(sessionHandle:long, intervalMin:double, intervalMax:double) : void - + {static} NormSetGrttProbingTOS(sessionHandle:long, probeTOS:byte) : void + {static} NormSetBackoffFactor(sessionHandle:long, backoffFactor:double) : void - + {static} NormSetGroupSize(sessionHandle:long, groupSize:uint) : void + + {static} NormSetGroupSize(sessionHandle:long, groupSize:long) : void + {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void - + {static} NormFileEnqueue(sessionHandle:long, fileName:string): long + {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long - + {static} NormDataEnqueue(sessionHandle:long, dataPtr:string, dataLen:int) : long - + {static} NormDataEnqueue(sessionHandle:long, dataPtr:string, dataLen:int, infoPtr:string, infoLen:int) : long + + {static} NormDataEnqueue(sessionHandle:long, dataPtr:byte[], dataLen:int, infoPtr:byte[], infoLen:int) : long + {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool - + {static} NormStreamOpen(sessionHandle:long, bufferSize:long) : long + {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long - + {static} NormStreamClose(streamHandle:long) : void + {static} NormStreamClose(streamHandle:long, graceful:bool) : void - + {static} NormGetStreamBufferSegmentCount(bufferBytes:uint, segmentSize:ushort, blockSize:ushort) : uint - + {static} NormStreamWrite(streamHandle:long, buffer:string, numBytes:uint) : uint - + {static} NormStreamFlush(streamHandle:long) : void + + {static} NormStreamWrite(streamHandle:long, buffer:byte[], numBytes:int) : int + {static} NormStreamFlush(streamHandle:long, eom:bool, flushMode:NormFlushMode) : void + {static} NormStreamSetAutoFlush(streamHandle:long, flushMode: NormFlushMode) : void + {static} NormStreamSetPushEnable(streamHandle:long, pushEnable:bool) : void + {static} NormStreamHasVacancy(streamHandle:long) : bool - + {static} NormStreamGetVacancy(streamHandle:long, bytesWanted:uint) : uint + {static} NormStreamMarkEom(streamHandle:long) : void - + {static} NormSetWatermark(sessionHandle:long, objectHandle:long) : bool + {static} NormSetWatermark(sessionHandle:long, objectHandle:long, overrideFlush:bool) : bool - + {static} NormSetWatermarkEx(sessionHandle:long, objectHandle:long, buffer:string, numBytes:uint) : bool - + {static} NormSetWatermarkEx(sessionHandle:long, objectHandle:long, buffer:string, numBytes:uint, overrideFlush:bool) : bool + {static} NormResetWatermark(sessionHandle:long) : bool + {static} NormCancelWatermark(sessionHandle:long) : void + {static} NormAddAckingNode(sessionHandle:long, nodeId:long) : bool + {static} NormRemoveAckingNode(sessionHandle:long, nodeId:long) : void - + {static} NormGetAckingNodeHandle(sessionHandle:long, nodeId:long) : long - + {static} NormSetAutoAckingNodes(sessionHandle:long, trackingStatus:NormTrackingStatus) : void - + {static} NormGetAckingStatus(sessionHandle:long) : NormAckingStatus + {static} NormGetAckingStatus(sessionHandle:long, nodeId:long) : NormAckingStatus - + {static} NormGetNextAckingNode(sessionHandle:long, nodeId:long) : bool - + {static} NormGetNextAckingNode(sessionHandle:long, nodeId:long, ackingStatus:NormAckingStatus) : bool - + {static} NormGetAckEx(sessionHandle:long, nodeId:long, buffer:string, buflen:uint) : bool - + {static} NormSendCommand(sessionHandle:long, cmdBuffer:string, cmdLength:uint) : bool - + {static} NormSendCommand(sessionHandle:long, cmdBuffer:string, cmdLength:uint, robust:bool) : bool + + {static} NormSendCommand(sessionHandle:long, cmdBuffer:byte[], cmdLength:int, robust:bool) : bool + {static} NormCancelCommand(sessionHandle:long) : void - + {static} NormSetSynStatus(sessionHandle:long, state:bool) : void + {static} NormStartReceiver(sessionHandle:long, bufferSpace:long) : bool + {static} NormStopReceiver(sessionHandle:long) : void - + {static} NormSetRxCacheLimit(sessionHandle:long, countMax:ushort) : void - + {static} NormSetRxSocketBuffer(sessionHandle:long, bufferSize:uint) : bool - + {static} NormSetSilentReceiver(sessionHandle:long, silent:bool) : void + + {static} NormSetRxCacheLimit(sessionHandle:long, countMax:int) : void + + {static} NormSetRxSocketBuffer(sessionHandle:long, bufferSize:long) : bool + {static} NormSetSilentReceiver(sessionHandle:long, silent:bool, maxDelay:int) : void + {static} NormSetDefaultUnicastNack(sessionHandle:long, unicastNacks:bool) : void + {static} NormNodeSetUnicastNack(remoteSender:long, unicastNacks:bool) : void @@ -135,17 +91,14 @@ class NormApi + {static} NormSetDefaultRepairBoundary(sessionHandle:long, repairBoundary:NormRepairBoundary) : void + {static} NormNodeSetRepairBoundary(remoteSender:long, repairBoundary:NormRepairBoundary) : void + {static} NormSetDefaultRxRobustFactor(sessionHandle:long, robustFactor:int) : void - + {static} NormNodeSetRxRobustFactor(remoteSenderremoteSender, robustFactor:int) : void - + {static} NormPreallocateRemoteSender(sessionHandle:long, bufferSize:ulong, segmentSize:ushort, numData:ushort, numParity:ushort) : bool - + {static} NormPreallocateRemoteSender(sessionHandle:long, bufferSize:ulong, segmentSize:ushort, numData:ushort, numParity:ushort, streamBufferSize:uint) : bool - + {static} NormStreamRead(streamHandle:long, buffer:string, numBytes:uint) : bool + + {static} NormNodeSetRxRobustFactor(remoteSender:long, robustFactor:int) : void + + {static} NormStreamRead(streamHandle:long, buffer:byte[], numBytes:int) : bool + {static} NormStreamSeekMsgStart(streamHandle:long) : bool - + {static} NormStreamGetReadOffset(streamHandle:long) : uint - + {static} NormStreamGetBufferUsage(streamHandle:long) : uint + + {static} NormStreamGetReadOffset(streamHandle:long) : long + {static} NormObjectGetType(objectHandle:long) : NormObjectType + {static} NormObjectHasInfo(objectHandle:long) : bool - + {static} NormObjectGetInfoLength(objectHandle:long) : ushort - + {static} NormObjectGetInfo(objectHandle:long, buffer:char, bufferLen:ushort) : ushort + + {static} NormObjectGetInfoLength(objectHandle:long) : int + + {static} NormObjectGetInfo(objectHandle:long, buffer:byte[], bufferLen:int) : int + {static} NormObjectGetSize(objectHandle:long) : int + {static} NormObjectGetBytesPending(objectHandle:long) : int + {static} NormObjectCancel(objectHandle:long) : void @@ -153,24 +106,14 @@ class NormApi + {static} NormObjectRelease(objectHandle:long) : void + {static} NormFileGetName(fileHandle:long, nameBuffer:string, bufferLen:int) : bool + {static} NormFileRename(fileHandle:long, fileName:string) : bool - + {static} NormDataAccessData(objectHandle:long) : string - + {static} NormDataDetachData(objectHandle:long) : string + + {static} NormDataAccessData(objectHandle:long) : nint + {static} NormObjectGetSender(objectHandle:long) : long - + {static} NormNodeGetId(nodeHandle:long) : uint - + {static} NormNodeGetAddress(nodeHandle:long, addrBuffer:string, bufferLen: uint) : bool - + {static} NormNodeGetAddress(nodeHandle:long, addrBuffer:string, bufferLen: uint, port:ushort) : bool + + {static} NormNodeGetId(nodeHandle:long) : long + + {static} NormNodeGetAddress(nodeHandle:long, addrBuffer:byte[], bufferLen: int, port:int) : bool + {static} NormNodeGetGrtt(remoteSender:long) : double - + {static} NormNodeGetCommand(remoteSender:long, :string, buflen:uint) : bool - + {static} NormNodeSendAckEx(remoteSender:long, buffer:string, numBytes:uint) : bool - + {static} NormNodeGetWatermarkEx(remoteSender:long, buffer:string, buflen:uint) : bool + + {static} NormNodeGetCommand(remoteSender:long, buffer:byte[], buflen:int) : bool + {static} NormNodeFreeBuffers(remoteSender:long) : void - + {static} NormNodeDelete(remoteSender:long) : void + {static} NormNodeRetain(nodeHandle:long) : void + {static} NormNodeRelease(nodeHandle:long) : void - + {static} NormReleasePreviousEvent(instanceHandle:long) : void - + {static} NormCountCompletedObjects(sessionHandle:long) : uint - + {static} NormNodeSetAutoDelete(remoteSender:long, autoDelete:bool) : void - + {static} NormNodeAllowSender(senderId:long) : bool - + {static} NormNodeDenySender(senderId:long) : bool } @enduml \ No newline at end of file diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml index 49e3c20f..b7c7f980 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml @@ -1,5 +1,6 @@ @startuml class NormFile { + + <> FILENAME_MAX : int = 260 ~ NormFile(handle:long) + Name : string <> + Rename(filename:string) : void diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml index 7aa9e8b3..6207ae75 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml @@ -1,5 +1,6 @@ @startuml class NormInstance { + + <> NORM_DESCRIPTOR_INVALID : int = 0 - _handle: long + NormInstance() + NormInstance(priorityBoost:bool) @@ -14,7 +15,9 @@ class NormInstance { + CloseDebugLog() : void + OpenDebugPipe(pipename:string) : void + DebugLevel: int <> <> - + HasNextEvent(sec:int, usec:int) : boolean + + HasNextEvent(sec:int, usec:int) : bool + + HasNextEvent(waitTime:TimeSpan) : bool + + GetNextEvent(waitForEvent:bool) : NormEvent + GetNextEvent() : NormEvent + CreateSession(address:string, port:int, localNodeId:long) : NormSession } diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml index d9757b9a..34a87df7 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml @@ -1,6 +1,8 @@ @startuml class NormNode { + <> NORM_NODE_ANY : long = 0xffffffff + + <> NORM_NODE_NONE : int = 0 + + <> NORM_NODE_INVALID : int = 0 - _handle : long ~ NormNode(handle:long) + SetUnicastNack(state:bool) : void @@ -8,9 +10,9 @@ class NormNode { + SetRepairBoundary(repairBoundary:NormRepairBoundary) : void + SetRxRobustFactor(robustFactor:int) : void + Id : long <> - + Address : SocketAddress <> + + Address : IPEndPoint <> + Grtt : double <> - + GetCommand(buffer:byte[], offset:int, length:int) : int + + Command : byte[] <> + FreeBuffers() : void + Retain() : void + Release(): void diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml index 2a48d175..3c2e8c13 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml @@ -1,5 +1,6 @@ @startuml class NormObject { + + <> NORM_OBJECT_INVALID : int = 0 # _handle:long ~ NormObject(handle:long) + SetNackingMode(nackingMode:NormNackingMode) : void diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml index e1fd0647..443590c3 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml @@ -1,5 +1,6 @@ @startuml class NormSession { + + <> NORM_SESSION_INVALID : int = 0 {static} -_normSessions : Dictionary - _handle : long ~ NormSession(handle:long) @@ -22,8 +23,10 @@ class NormSession { + SetTxLoss(precent:double) : void + SetRxLoss(precent:double) : void + ReportInterval: double <> <> - + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void + + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void + + StartSender(bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void + + StartSender(bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void + StopSender() : void + SetTxOnly(txOnly:bool) : void + TxRate:double <> <> @@ -43,11 +46,12 @@ class NormSession { + SetTxRobustFactor(robustFactor:int) : void + FileEnqueue(filename:string) : NormFile + FileEnqueue(filename:string, info:byte[], infoOffset:int, infoLength:int) : NormFile - + DataEnqueue(dataBuffer:MemoryStream, dataOffset:int, dataLength:int) : NormData - + DataEnqueue(dataBuffer:MemoryStream, dataOffset:int, dataLength:int, info:byte[], infoOffset:int, infoLength:int) : NormData + + DataEnqueue(dataBuffer:byte[], dataLength:int) : NormData + + DataEnqueue(dataBuffer:byte[], dataOffset:int, dataLength:int, info:byte[], infoOffset:int, infoLength:int) : NormData + RequeueObject(object:NormObject) : void + StreamOpen(bufferSize:long) : NormStream + StreamOpen(bufferSize:long, info:byte[], infoOffset:int, infoLength:int) : NormStream + + SetWatermark(object:NormObject) : void + SetWatermark(object:NormObject, overrideFlush:bool) : void + CancelWatermark() : void + ResetWatermark() : void diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml index e4c1831a..bb06864b 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml @@ -14,4 +14,5 @@ class NormStream { + SeekMsgStart() : bool + ReadOffset : long <> } +NormObject <|-- NormStream @enduml \ No newline at end of file diff --git a/src/dotnet/design/SequenceDiagrams/NormFileSend.puml b/src/dotnet/design/SequenceDiagrams/NormFileSend.puml index 8f1e7630..4e24294a 100644 --- a/src/dotnet/design/SequenceDiagrams/NormFileSend.puml +++ b/src/dotnet/design/SequenceDiagrams/NormFileSend.puml @@ -7,7 +7,7 @@ Client <- NormInstance : NormInstance Client -> NormInstance : CreateSession(address, port, localNodeId) NormInstance -> NormApi : NormCreateSession(instanceHandle, address, port, localNodeId) NormInstance <- NormApi : sessionHandle -alt sessionHandle == NORM_SESSION_INVALID +alt sessionHandle == NormApi.NORM_SESSION_INVALID NormInstance -> IOException : throw new("Failed to create session") end NormInstance -> NormSession : new(sessionHandle) @@ -30,9 +30,9 @@ end NormSession -> NormFile: new(objectHandle) NormSession <- NormFile : NormFile Client <- NormSession : NormFile -loop NormEvent != null - Client -> NormInstance : GetNextEvent() - NormInstance -> NormApi : NormGetNextEvent(instanceHandle, NormEvent) +loop NormInstance.HasNextEvent(waitTime) + Client -> NormInstance : GetNextEvent(false) + NormInstance -> NormApi : NormGetNextEvent(instanceHandle, NormEvent, false) NormInstance <- NormApi : success alt success == false Client <- NormInstance : null diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormAckingStatus.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormAckingStatus.cs new file mode 100644 index 00000000..2e98cc1f --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormAckingStatus.cs @@ -0,0 +1,28 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The status of the watermark flushing process and/or positive acknowledgment collection. + /// + public enum NormAckingStatus + { + /// + /// The given sessionHandle is invalid or the given nodeId is not in the sender's acking list. + /// + NORM_ACK_INVALID, + /// + /// The positive acknowledgement collection process did not receive acknowledgment from every + /// listed receiver (nodeId = NORM_NODE_ANY) or the identified nodeId did not respond. + /// + NORM_ACK_FAILURE, + /// + /// The flushing process at large has not yet completed (nodeId = NORM_NODE_ANY) or the given + /// individual nodeId is still being queried for response. + /// + NORM_ACK_PENDING, + /// + /// All receivers (nodeId = NORM_NODE_ANY) responded with positive acknowledgement or the given + /// specific nodeId did acknowledge. + /// + NORM_ACK_SUCCESS + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormEventType.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormEventType.cs new file mode 100644 index 00000000..f968e06b --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormEventType.cs @@ -0,0 +1,194 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The type identifies the event with one of NORM protocol events. + /// + public enum NormEventType + { + /// + /// This NormEventType indicates an invalid or "null" notification which should be ignored. + /// + NORM_EVENT_INVALID, + /// + /// This event indicates that there is room for additional transmit objects to be enqueued, or, + /// if the handle of NORM_OBJECT_STREAM is given in the corresponding + /// event "object" field, the application may successfully write to the indicated stream + /// object. Note this event is not dispatched until a call to NormFileEnqueue(), + /// NormDataEnqueue(), or NormStreamWrite() fails because of a filled transmit + /// cache or stream buffer. + /// + NORM_TX_QUEUE_VACANCY, + /// + /// This event indicates the NORM protocol engine has no new data pending transmission + /// and the application may enqueue additional objects for transmission. + /// If the handle of a sender NORM_OBJECT_STREAM is given in the corresponding event + /// "object" field, this indicates the stream transmit buffer has been emptied and the + /// sender application may write to the stream (Use of NORM_TX_QUEUE_VACANCY may + /// be preferred for this purpose since it allows the application to keep the NORM + /// protocol engine busier sending data, resulting in higher throughput when attempting + /// very high transfer rates). + /// + NORM_TX_QUEUE_EMPTY, + /// + /// This event indicates that the flushing process the NORM sender observes when + /// it no longer has data ready for transmission has completed. The completion of the + /// flushing process is a reasonable indicator (with a sufficient NORM "robust factor" + /// value) that the receiver set no longer has any pending repair requests. Note the + /// use of NORM's optional positive acknowledgement feature is more deterministic + /// in this regards, but this notification is useful when there are non-acking (NACK-only) + /// receivers. The default NORM robust factor of 20 (20 flush messages are + /// sent at end-of-transmission) provides a high assurance of reliable transmission, + /// even with packet loss rates of 50%. + /// + NORM_TX_FLUSH_COMPLETED, + /// + /// This event indicates that the flushing process initiated by a prior application call + /// to NormSetWatermark() has completed. The posting of this event indicates the + /// appropriate time for the application to make a call NormGetAckingStatus() to + /// determine the results of the watermark flushing process. + /// + NORM_TX_WATERMARK_COMPLETED, + /// + /// This event indicates that an application-defined command previously enqueued + /// with a call to NormSendCommand() has been transmitted, including any repetition. + /// + NORM_TX_CMD_SENT, + /// + /// This event indicates that the transport object referenced by the event's "object" + /// field has completed at least one pass of total transmission. Note that this does not + /// guarantee that reliable transmission has yet completed; only that the entire object + /// content has been transmitted. Depending upon network behavior, several rounds + /// of NACKing and repair transmissions may be required to complete reliable transfer. + /// + NORM_TX_OBJECT_SENT, + /// + /// This event indicates that the NORM protocol engine will no longer refer to the + /// transport object identified by the event's "object" field. Typically, this will occur + /// when the application has enqueued more objects than space available within the + /// set sender transmit cache bounds. Posting of this notification means the application is + /// free to free any resources (memory, files, etc) associated with the indicated "object". + /// After this event, the given "object" handle (NormObjectHandle) is no longer valid unless + /// it is specifically retained by the application. + /// + NORM_TX_OBJECT_PURGED, + /// + /// This event indicates that NORM Congestion Control operation has adjusted the + /// transmission rate. The NormGetTxRate() call may be used to retrieve the new + /// corresponding transmission rate. Note that if NormSetCongestionControl() was + /// called with its adjustRate parameter set to false, then no actual rate change has + /// occurred and the rate value returned by NormGetTxRate() reflects a "suggested" + /// rate and not the actual transmission rate. + /// + NORM_TX_RATE_CHANGED, + /// + /// This event is posted when the NORM protocol engine completes the "graceful + /// shutdown" of its participation as a sender in the indicated "session". + /// + NORM_LOCAL_SENDER_CLOSED, + /// + /// This event is posted when a receiver first receives messages from a specific remote + /// NORM sender. This marks the beginning of the interval during which the application + /// may reference the provided "node" handle (NormNodeHandle). + /// + NORM_REMOTE_SENDER_NEW, + /// + /// Remote sender instanceId or FEC params changed. + /// + NORM_REMOTE_SENDER_RESET, + /// + /// Remote sender src addr and/or port changed. + /// + NORM_REMOTE_SENDER_ADDRESS, + /// + /// This event is posted when a previously inactive (or new) remote sender is detected + /// operating as an active sender within the session. + /// + NORM_REMOTE_SENDER_ACTIVE, + /// + /// This event is posted after a significant period of inactivity (no sender messages + /// received) of a specific NORM sender within the session. The NORM protocol + /// engine frees buffering resources allocated for this sender when it becomes inactive. + /// + NORM_REMOTE_SENDER_INACTIVE, + /// + /// This event is posted when the NORM protocol engine frees resources for, and + /// thus invalidates the indicated "node" handle. + /// + NORM_REMOTE_SENDER_PURGED, + /// + /// This event indicates that an application-defined command has been received from + /// a remote sender. The NormEvent node element indicates the NormNodeHandle + /// value associated with the given sender. The NormNodeGetCommand() call can be + /// used to retrieve the received command content. + /// + NORM_RX_CMD_NEW, + /// + /// This event is posted when reception of a new transport object begins and marks + /// the beginning of the interval during which the specified "object" (NormObjectHandle) + /// is valid. + /// + NORM_RX_OBJECT_NEW, + /// + /// This notification is posted when the NORM_INFO content for the indicated "object" is received. + /// + NORM_RX_OBJECT_INFO, + /// + /// This event indicates that the identified receive "object" has newly received data content. + /// + NORM_RX_OBJECT_UPDATED, + /// + /// This event is posted when a receive object is completely received, including + /// available NORM_INFO content. Unless the application specifically retains the "object" + /// handle, the indicated NormObjectHandle becomes invalid and must no longer be + /// referenced. + /// + NORM_RX_OBJECT_COMPLETED, + /// + /// This notification is posted when a pending receive object's transmission is aborted + /// by the remote sender. Unless the application specifically retains the "object" + /// handle, the indicated NormObjectHandle becomes invalid and must no longer be + /// referenced. + /// + NORM_RX_OBJECT_ABORTED, + /// + /// Upon receipt of app-extended watermark ack request. + /// + NORM_RX_ACK_REQUEST, + /// + /// This notification indicates that either the local sender estimate of GRTT has + /// changed, or that a remote sender's estimate of GRTT has changed. The "sender" + /// member of the NormEvent is set to NORM_NODE_INVALID if the local sender's + /// GRTT estimate has changed or to the NormNodeHandle of the remote sender that + /// has updated its estimate of GRTT + /// + NORM_GRTT_UPDATED, + /// + /// This event indicates that congestion control feedback from receivers has begun + /// to be received (This also implies that receivers in the group are actually present + /// and can be used as a cue to begin data transmission.). Note that congestion control + /// must be enabled for this event to be posted. + /// Congestion control feedback can be assumed to be received until a NORM_CC_INACTIVE event is posted. + /// + NORM_CC_ACTIVE, + /// + /// This event indicates there has been no recent congestion control feedback received + /// from the receiver set and that the local NORM sender has reached its minimum + /// transmit rate. Applications may wish to refrain from new data transmission until + /// a NORM_CC_ACTIVE event is posted. This notification is only posted when congestion + /// control operation is enabled and a previous NORM_CC_ACTIVE event has occurred. + /// + NORM_CC_INACTIVE, + /// + /// When NormSetAutoAcking. + /// + NORM_ACKING_NODE_NEW, + /// + /// ICMP error (e.g. destination unreachable). + /// + NORM_SEND_ERROR, + /// + /// Issues when timeout set by NormSetUserTimer() expires. + /// + NORM_USER_TIMEOUT + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFecType.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFecType.cs new file mode 100644 index 00000000..6bb6133d --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFecType.cs @@ -0,0 +1,21 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The valid FEC Types. + /// + public enum NormFecType + { + /// + /// Fully-specified, general purpose Reed-Solomon. + /// + RS, + /// + /// Fully-specified 8-bit Reed-Solmon per RFC 5510. + /// + RS8, + /// + /// Partially-specified "small block" codes. + /// + SB + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFlushMode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFlushMode.cs new file mode 100644 index 00000000..2212a5cb --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormFlushMode.cs @@ -0,0 +1,22 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The possible flush modes. + /// + public enum NormFlushMode + { + /// + /// No flushing occurs unless explicitly requested via NormStreamFlush(). + /// + NORM_FLUSH_NONE, + /// + /// Causes NORM to immediately transmit all enqueued data for the stream (subject to session transmit rate limits), + /// even if this results in NORM_DATA messages with "small" payloads. + /// + NORM_FLUSH_PASSIVE, + /// + /// The sender actively transmits NORM_CMD(FLUSH) messages after any enqueued stream content has been sent. + /// + NORM_FLUSH_ACTIVE + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormNackingMode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormNackingMode.cs new file mode 100644 index 00000000..dadd8518 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormNackingMode.cs @@ -0,0 +1,21 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The available nacking modes. + /// + public enum NormNackingMode + { + /// + /// Do not transmit any repair requests for the newly received object. + /// + NORM_NACK_NONE, + /// + /// Transmit repair requests for NORM_INFO content only as needed. + /// + NORM_NACK_INFO_ONLY, + /// + /// Transmit repair requests for entire object as needed. + /// + NORM_NACK_NORMAL + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormObjectType.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormObjectType.cs new file mode 100644 index 00000000..5c571407 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormObjectType.cs @@ -0,0 +1,25 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The possible NORM data transport object types. + /// + public enum NormObjectType + { + /// + /// A special NormObjectType value, NORM_OBJECT_NONE, indicates an invalid object type. + /// + NORM_OBJECT_NONE, + /// + /// A transport object of type NORM_OBJECT_DATA. + /// + NORM_OBJECT_DATA, + /// + /// A transport object of type NORM_OBJECT_FILE. + /// + NORM_OBJECT_FILE, + /// + /// A transport object of type NORM_OBJECT_STREAM. + /// + NORM_OBJECT_STREAM + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormProbingMode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormProbingMode.cs new file mode 100644 index 00000000..f48ebb36 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormProbingMode.cs @@ -0,0 +1,24 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The possible probing modes. + /// + public enum NormProbingMode + { + /// + /// The sender application must explicitly set its estimate of GRTT using the NormSetGrttEstimate() function. + /// + NORM_PROBE_NONE, + /// + /// The NORM sender still transmits NORM_CMD(CC) probe messages multiplexed with its data transmission, + /// but the receiver set does not explicitly acknowledge these probes. Instead the receiver set is limited + /// to opportunistically piggy-backing responses when NORM_NACK messages are generated. + /// + NORM_PROBE_PASSIVE, + /// + /// In this mode, the receiver set explicitly acknowledges NORM sender GRTT probes ((NORM_CMD(CC) messages) + /// with NORM_ACK responses that are group-wise suppressed. + /// + NORM_PROBE_ACTIVE + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormRepairBoundary.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormRepairBoundary.cs new file mode 100644 index 00000000..430fc883 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormRepairBoundary.cs @@ -0,0 +1,22 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The possible for values for NORM repair boundary. + /// + /// + /// Customizes at what points the receiver initiates the NORM NACK repair process during protocol operation. + /// + public enum NormRepairBoundary + { + /// + /// For smaller block sizes, the NACK repair process is often/quickly initiated and the + /// repair of an object will occur, as needed, during the transmission of the object. + /// + NORM_BOUNDARY_BLOCK, + /// + /// Causes the protocol to defer NACK process initiation until the current transport object + /// has been completely transmitted. + /// + NORM_BOUNDARY_OBJECT + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormSyncPolicy.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormSyncPolicy.cs new file mode 100644 index 00000000..deb54b54 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Enums/NormSyncPolicy.cs @@ -0,0 +1,22 @@ +namespace Mil.Navy.Nrl.Norm.Enums +{ + /// + /// The possible synchronization policies. + /// + public enum NormSyncPolicy + { + /// + /// Attempt reception of "current" and new objects only (default). + /// + NORM_SYNC_CURRENT, + /// + /// Sync to current stream, but to beginning of stream. + /// + NORM_SYNC_STREAM, + /// + /// Attempt recovery and reliable reception of all objects + /// held in sender transmit object cache and newer objects. + /// + NORM_SYNC_ALL + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/INormEventListener.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/INormEventListener.cs new file mode 100644 index 00000000..d5fea394 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/INormEventListener.cs @@ -0,0 +1,7 @@ +namespace Mil.Navy.Nrl.Norm.IO +{ + public interface INormEventListener + { + void NormEventOccurred(NormEvent normEvent); + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormInputStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormInputStream.cs new file mode 100644 index 00000000..0272df91 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormInputStream.cs @@ -0,0 +1,332 @@ +using System.Runtime.CompilerServices; + +namespace Mil.Navy.Nrl.Norm.IO +{ + public class NormInputStream : Stream + { + private NormInstance _normInstance; + private NormSession _normSession; + private NormStream? _normStream; + + private List _normEventListeners; + + private bool _closed; + private object _closeLock; + + private bool _bufferIsEmpty; + private bool _receivedEof; + + /// + public NormInputStream(string address, int port) + { + // Create the NORM instance + _normInstance = new NormInstance(); + + // Create the NORM session + _normSession = _normInstance.CreateSession(address, port, NormNode.NORM_NODE_ANY); + _normStream = null; + + _normEventListeners = new List(); + + _closed = true; + _closeLock = new object(); + + _bufferIsEmpty = true; + _receivedEof = false; + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void OpenDebugLog(string fileName) + { + if (fileName == null) + { + throw new IOException("File was name was not found."); + } + _normInstance.OpenDebugLog(fileName); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void CloseDebugLog() => _normInstance.CloseDebugLog(); + + [MethodImpl(MethodImplOptions.Synchronized)] + public void NormSetDebugLevel(int level) { _normInstance.DebugLevel = level; } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetMessageTrace(bool messageTrace) => _normSession.SetMessageTrace(messageTrace); + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetMulticastInterface(string multicastInterface) + { + _normSession.SetMulticastInterface(multicastInterface); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetEcnSupport(bool ecnEnable, bool ignoreLoss) + { + _normSession.SetEcnSupport(ecnEnable, ignoreLoss); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetTtl(byte ttl) + { + _normSession.SetTTL(ttl); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetTos(byte tos) + { + _normSession.SetTOS(tos); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void setSilentReceiver(bool silent, int maxDelay) + { + _normSession.SetSilentReceiver(silent, maxDelay); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetDefaultUnicastNack(bool defaultUnicastNack) + { + _normSession.SetDefaultUnicastNack(defaultUnicastNack); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SeekMsgStart() + { + if (_normStream == null) + { + throw new InvalidOperationException("Can only seek msg start after the stream is connected"); + } + + _normStream.SeekMsgStart(); + } + + /// The INormEventListener to add. + [MethodImpl(MethodImplOptions.Synchronized)] + public void AddNormEventListener(INormEventListener normEventListener) + { + lock (_normEventListeners) + { + _normEventListeners.Add(normEventListener); + } + } + + /// The INormEventListener to remove. + [MethodImpl(MethodImplOptions.Synchronized)] + public void RemoveNormEventListener(INormEventListener normEventListener) + { + lock (_normEventListeners) + { + _normEventListeners.Remove(normEventListener); + } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + private void FireNormEventOccured(NormEvent normEvent) + { + lock (_normEventListeners) + { + foreach (var normEventListener in _normEventListeners) + { + normEventListener.NormEventOccurred(normEvent); + } + + } + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void Open(long bufferSpace) + { + lock (_closeLock) + { + if (!IsClosed) + { + throw new IOException("Stream is already open"); + } + + _normSession.StartReceiver(bufferSpace); + + _closed = false; + } + } + + /// + public override void Close() + { + lock (_closeLock) + { + if (IsClosed) + { + return; + } + + _normStream?.Close(false); + _normSession.StopSender(); + _normInstance.StopInstance(); + + _closed = true; + } + } + + public bool IsClosed + { + get + { + lock (_closeLock) + { + return _closed; + } + } + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public int Read() + { + var buffer = new byte[1]; + + if (IsClosed) + { + throw new IOException("Stream is closed"); + } + + if (Read(buffer) < 0) + { + return -1; + } + + return buffer[0]; + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public override int Read(byte[] buffer, int offset, int count) + { + int n; + + if (IsClosed) + { + throw new IOException("Stream is closed"); + } + + do + { + while (_bufferIsEmpty || _normInstance.HasNextEvent(0, 0)) + { + ProcessEvent(); + + if (_receivedEof) + { + return -1; + } + } + + if (_normStream == null) + { + return -1; + } + + // Read from the stream + if ((n = _normStream.Read(buffer, offset, count)) < 0) + { + throw new IOException("Break in stream integrity"); + } + + _bufferIsEmpty = n == 0; + } + while (_bufferIsEmpty); + + return n; + } + + private void ProcessEvent() + { + // Retrieve the next event + var normEvent = _normInstance.GetNextEvent(); + + // Check if the stream was closed + if (IsClosed) + { + throw new IOException("Stream closed"); + } + + if (normEvent != null) + { + // Process the event + var eventType = normEvent.Type; + switch (eventType) + { + case NormEventType.NORM_RX_OBJECT_NEW: + var normObject = normEvent.Object; + if (normObject != null && normObject.Type == NormObjectType.NORM_OBJECT_STREAM) + { + _normStream = (NormStream)normObject; + } + break; + + case NormEventType.NORM_RX_OBJECT_UPDATED: + var theNormObject = normEvent.Object; + if (theNormObject == null || !theNormObject.Equals(_normStream)) + { + break; + } + + // Signal that the buffer is not empty + _bufferIsEmpty = false; + break; + + case NormEventType.NORM_RX_OBJECT_ABORTED: + case NormEventType.NORM_RX_OBJECT_COMPLETED: + _normStream = null; + + // Signal that the stream has ended + _receivedEof = true; + break; + + default: + break; + } + + // Notify listeners of the norm event + FireNormEventOccured(normEvent); + } + } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormOutputStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormOutputStream.cs new file mode 100644 index 00000000..5c4481d2 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/NormOutputStream.cs @@ -0,0 +1,392 @@ +using System.Runtime.CompilerServices; + +namespace Mil.Navy.Nrl.Norm.IO +{ + public class NormOutputStream : Stream + { + private NormInstance _normInstance; + private NormSession _normSession; + private NormStream? _normStream; + + private List _normEventListeners; + + private bool _closed; + private object _closeLock; + + private bool _bufferIsFull; + + /// + public NormOutputStream(string address, int port) + { + // Create the NORM instance + _normInstance = new NormInstance(); + + // Create the NORM session + _normSession = _normInstance.CreateSession(address, port, NormNode.NORM_NODE_ANY); + + _normStream = null; + + _normEventListeners = new List(); + + _closed = true; + _closeLock = new object(); + + _bufferIsFull = false; + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void OpenDebugLog(string filename) + { + _normInstance.OpenDebugLog(filename); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void CloseDebugLog() + { + _normInstance.CloseDebugLog(); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetDebugLevel(int level) + { + _normInstance.DebugLevel = level; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetMessageTrace(bool messageTrace) + { + _normSession.SetMessageTrace(messageTrace); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetMulticastInterface(string multicastInterface) + { + _normSession.SetMulticastInterface(multicastInterface); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetEcnSupport(bool ecnEnable, bool ignoreLoss) + { + _normSession.SetEcnSupport(ecnEnable, ignoreLoss); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetTtl(byte ttl) + { + _normSession.SetTTL(ttl); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetTos(byte tos) + { + _normSession.SetTOS(tos); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetCongestionControl(bool ccEnabled, bool ccAdjustRate) + { + _normSession.SetCongestionControl(ccEnabled, ccAdjustRate); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetTxRateBounds(double minTxRate, double maxTxRate) + { + _normSession.SetTxRateBounds(maxTxRate, minTxRate); + } + + public double TxRate + { + get + { + lock (this) + { + return _normSession.TxRate; + } + } + set + { + lock (this) + { + _normSession.TxRate = value; + } + } + } + + public double GrttEstimate + { + get + { + lock (this) + { + return _normSession.GrttEstimate; + } + } + set + { + lock (this) + { + _normSession.GrttEstimate = value; + } + } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetGroupSize(long groupSize) + { + _normSession.SetGroupSize(groupSize); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetAutoParity(short autoParity) + { + _normSession.SetAutoParity(autoParity); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetBackoffFactor(double backoffFactor) + { + _normSession.SetBackoffFactor(backoffFactor); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetAutoFlush(NormFlushMode flushMode) + { + if (_normStream == null) + { + throw new InvalidOperationException("Can only set auto flush after the stream is open"); + } + _normStream.SetAutoFlush(flushMode); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetPushEnable(bool pushEnable) + { + if (_normStream == null) + { + throw new InvalidOperationException("Can only set push enabled after the stream is open"); + } + _normStream.SetPushEnable(pushEnable); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void MarkEom() + { + if (_normStream == null) + { + throw new InvalidOperationException("Can only mark EOM after the stream is open"); + } + _normStream.MarkEom(); + } + + /// The INormEventListener to add. + public void AddNormEventListener(INormEventListener normEventListener) + { + lock (_normEventListeners) + { + _normEventListeners.Add(normEventListener); + } + } + + /// The INormEventListener to remove. + public void RemoveNormEventListener(INormEventListener normEventListener) + { + lock (_normEventListeners) + { + _normEventListeners.Remove(normEventListener); + } + } + + private void FireNormEventOccured(NormEvent normEvent) + { + lock (_normEventListeners) + { + foreach (var normEventListener in _normEventListeners) + { + normEventListener.NormEventOccurred(normEvent); + } + } + } + + /// + public void Open(int sessionId, long bufferSpace, int segmentSize, short blockSize, short numParity, long repairWindow) + { + lock (_closeLock) + { + if (IsClosed) + { + throw new IOException("Stream is already open"); + } + + _normSession.StartSender(sessionId, bufferSpace, segmentSize, blockSize, numParity); + + // Open the stream + _normStream = _normSession.StreamOpen(repairWindow); + + _closed = false; + } + } + + /// + public override void Close() + { + lock (_closeLock) + { + if (IsClosed) + { + return; + } + + _normStream?.Close(false); + _normSession.StopSender(); + _normInstance.StopInstance(); + + _closed = true; + } + } + + public bool IsClosed + { + get + { + lock (_closeLock) + { + return _closed; + } + } + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public void Write(int b) + { + if (IsClosed) + { + throw new IOException("Stream is closed"); + } + + var buffer = new byte[1]; + buffer[0] = (byte)b; + + Write(buffer); + } + + /// + [MethodImpl(MethodImplOptions.Synchronized)] + public override void Write(byte[] buffer, int offset, int count) + { + int n; + + if (IsClosed) + { + throw new IOException("Stream is closed"); + } + + while (count > 0) + { + while (_normInstance.HasNextEvent(0, 0)) + { + ProcessEvent(); + } + + // Wait while the buffer is full + while (_bufferIsFull) + { + ProcessEvent(); + } + + // Write some data + if (_normStream == null || (n = _normStream.Write(buffer, offset, count)) < 0) + { + throw new IOException("Failed to write to stream"); + } + + _bufferIsFull = n == 0; + + count -= n; + offset += n; + } + } + + /// + private void ProcessEvent() + { + // Retrieve the next event + var normEvent = _normInstance.GetNextEvent(); + + // Check if the stream was closed + if (IsClosed) + { + throw new IOException("Stream closed"); + } + + if (normEvent != null) + { + // Process the event + var eventType = normEvent.Type; + switch (eventType) + { + case NormEventType.NORM_TX_QUEUE_VACANCY: + case NormEventType.NORM_TX_QUEUE_EMPTY: + var normObject = normEvent.Object; + if (normObject == null || !normObject.Equals(_normStream)) + { + break; + } + + // Signal that the buffer is not full + _bufferIsFull = false; + break; + + case NormEventType.NORM_TX_OBJECT_SENT: + case NormEventType.NORM_TX_OBJECT_PURGED: + _normStream = null; + break; + + default: + break; + } + + // Notify listeners of the norm event + FireNormEventOccured(normEvent); + } + } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => IsClosed; + + public override long Length => throw new NotSupportedException(); + + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/StreamBreakException.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/StreamBreakException.cs new file mode 100644 index 00000000..b4b6998e --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/IO/StreamBreakException.cs @@ -0,0 +1,9 @@ +namespace Mil.Navy.Nrl.Norm.IO +{ + public class StreamBreakException : IOException + { + public StreamBreakException(string? message) : base(message) + { + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj b/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj new file mode 100644 index 00000000..efa14686 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj @@ -0,0 +1,10 @@ + + + + net6.0 + enable + enable + 1.0.0 + + + diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs new file mode 100644 index 00000000..e8bc6db0 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -0,0 +1,1133 @@ +using System.Runtime.InteropServices; + +namespace Mil.Navy.Nrl.Norm +{ + /// + /// The native NORM API functions + /// + public static class NormApi + { + /// + /// The name of the NORM library used when calling native NORM API functions + /// + public const string NORM_LIBRARY = "norm"; + + /// + /// The NormEvent type is a structure used to describe significant NORM protocol events. + /// + [StructLayout(LayoutKind.Sequential)] + public struct NormEvent + { + /// + /// The Type field indicates the NormEventType and determines how the other fields should be interpreted. + /// + public NormEventType Type; + /// + /// The Session field indicates the applicable NormSessionHandle to which the event applies. + /// + public long Session; + /// + /// The Sender field indicates the applicable NormNodeHandle to which the event applies. + /// + public long Sender; + /// + /// The Object field indicates the applicable NormObjectHandle to which the event applies. + /// + public long Object; + } + + /// + /// This function creates an instance of a NORM protocol engine and is the necessary first step before any other API functions may be used. + /// + /// The priorityBoost parameter, when set to a value of true, specifies that the NORM protocol engine thread be run with higher priority scheduling. + /// A value of NORM_INSTANCE_INVALID is returned upon failure. + [DllImport(NORM_LIBRARY)] + public static extern long NormCreateInstance(bool priorityBoost); + + /// + /// The NormDestroyInstance() function immediately shuts down and destroys the NORM protocol engine instance referred to by the instanceHandle parameter. + /// + /// The NORM protocol engine instance referred to by the instanceHandle parameter. + [DllImport(NORM_LIBRARY)] + public static extern void NormDestroyInstance(long instanceHandle); + + /// + /// The NormStopInstance() this function immediately stops the NORM protocol engine thread corresponding to the given instanceHandle parameter. + /// + /// The NORM protocol engine instance referred to by the instanceHandle parameter. + [DllImport (NORM_LIBRARY)] + public static extern void NormStopInstance(long instanceHandle); + + /// + /// The NormRestartInstance() this function creates and starts an operating system thread to resume NORM protocol engine operation for the given + /// instanceHandle that was previously stopped by a call to NormStopInstance(). + /// + /// The NORM protocol engine instance referred to by the instanceHandle parameter. + /// Boolean as to the success of the instance restart. + [DllImport (NORM_LIBRARY)] + public static extern bool NormRestartInstance(long instanceHandle); + + /// + /// The NormSuspendInstance() immediately suspends the NORM protocol engine thread corresponding to the given instanceHandle parameter + /// + /// The NORM protocol engine instance referred to by the instanceHandle parameter. + /// Boolean as to the success of the instance suspension. + [DllImport (NORM_LIBRARY)] + public static extern bool NormSuspendInstance(long instanceHandle); + + /// + /// Resumes NORM protocol engine thread corresponding to the given instanceHandler parameter. + /// + /// The NORM protocol engine instance referred to by the instanceHandle parameter. + /// Boolean as to the success of the instance resumption. + [DllImport (NORM_LIBRARY)] + public static extern bool NormResumeInstance(long instanceHandle); + + /// + /// This function sets the directory path used by receivers to cache newly-received NORM_OBJECT_FILE content. + /// + /// The instanceHandle parameter specifies the NORM protocol engine instance + /// (all NormSessions associated with that instanceHandle share the same cache path). + /// the cachePath is a string specifying a valid (and writable) directory path. + /// The function returns true on success and false on failure. The failure conditions are + /// that the indicated directory does not exist or the process does not have permissions to write. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetCacheDirectory(long instanceHandle, string cachePath); + + /// + /// This function retrieves the next available NORM protocol event from the protocol engine. + /// + /// The instanceHandle parameter specifies the applicable NORM protocol engine. + /// The parameter must be a valid pointer to a NormEvent structure capable of receiving the NORM event information. + /// waitForEvent specifies whether the call to this function is blocking or not, + /// if "waitForEvent" is false, this is a non-blocking call. + /// The function returns true when a NormEvent is successfully retrieved, and false otherwise. + /// Note that a return value of false does not indicate an error or signify end of NORM operation. + [DllImport(NORM_LIBRARY)] + public static extern bool NormGetNextEvent(long instanceHandle, out NormEvent theEvent, bool waitForEvent); + + /// + /// This function is used to retrieve a NormDescriptor (Unix int file descriptor or Win32 HANDLE) suitable for + /// asynchronous I/O notification to avoid blocking calls to NormGetNextEvent(). + /// + /// The NORM protocol engine instance referred to by the instanceHandle parameter. + /// A NormDescriptor value is returned which is valid until a call to NormDestroyInstance() is made. + /// Upon error, a value of NORM_DESCRIPTOR_INVALID is returned. + [DllImport(NORM_LIBRARY)] + public static extern int NormGetDescriptor(long instanceHandle); + + /// + /// This function creates a NORM protocol session (NormSession) using the address (multicast or unicast) and port + /// parameters provided.While session state is allocated and initialized, active session participation does not begin + /// until a call is made to NormStartSender() and/or NormStartReceiver() to join the specified multicast group + /// (if applicable) and start protocol operation. + /// + /// Valid NormInstanceHandle previously obtained with a call to NormCreateInstance(). + /// Specified address determines the destination of NORM messages sent. + /// Valid, unused port number corresponding to the desired NORM session address. + /// Identifies the application's presence in the NormSession. + /// Returns a session handle. + [DllImport(NORM_LIBRARY)] + public static extern long NormCreateSession(long instanceHandle, string sessionAddress, int sessionPort, long localNodeId); + + /// + /// This function immediately terminates the application's participation in the NormSession and frees any resources used by that session. + /// + /// Used to identify application in the NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormDestroySession(long sessionHandle); + + /// + /// This function retrieves the NormNodeId value used for the application's participation in the NormSession. + /// + /// Used to identify application in the NormSession. + /// The returned value indicates the NormNode identifier used by the NORM protocol engine for the local application's participation in the specified NormSession. + [DllImport(NORM_LIBRARY)] + public static extern long NormGetLocalNodeId(long sessionHandle); + + /// + /// This function is used to force NORM to use a specific port number for UDP packets sent for the specified sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The txPortNumber parameter, specifies which port number to use. + /// The enableReuse parameter, when set to true, allows that the specified port may be reused for multiple sessions. + /// The txBindAddress parameter allows specification of a specific source address binding for packet transmission. + /// This function returns true upon success and false upon failure. Failure will occur if a txBindAddress is providedthat does not + /// correspond to a valid, configured IP address for the local host system. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetTxPort(long sessionHandle, int txPortNumber, bool enableReuse, string? txBindAddress); + + /// + /// This function limits the NormSession to perform NORM sender functions only. + /// + /// Used to identify application in the NormSession. + /// Boolean specifing whether to turn on or off the txOnly operation. + /// The optional connectToSessionAddress parameter, when set to true, + /// causes the underlying NORM code to "connect()" the UDP socket to the session (remote receiver) address and port number. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetTxOnly(long sessionHandle, bool txOnly, bool connectToSessionAddress); + + /// + /// This function allows the user to control the port reuse and binding behavior for the receive socket used for the given NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// When the enablReuse parameter is set to true, reuse of the NormSession port number by multiple NORM instances or sessions is enabled. + /// If the optional rxBindAddress is supplied (an IP address or host name in string form), the socket will bind() to the given address + /// when it is opened in a call to NormStartReceiver() or NormStartSender(). + /// The optional senderAddress parameter can be used to connect() the underlying NORM receive socket to specific address. + /// The optional senderPort parameter can be used to connect() the underlying NORM receive socket to specific port. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetRxPortReuse(long sessionHandle, bool enableReuse, string? rxBindAddress, string? senderAddress, int senderPort); + + /// Used to identify application in the NormSession. + /// Enables NORM ECN (congestion control) support. + /// With "ecnEnable", use ECN-only, ignoring packet loss. + /// Loss-tolerant congestion control, ecnEnable or not. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetEcnSupport(long sessionHandle, bool ecnEnable, bool ignoreLoss, bool tolerateLoss); + + /// + /// This function specifies which host network interface is used for IP Multicast transmissions and group membership. + /// This should be called before any call to NormStartSender() or NormStartReceiver() is made so that the IP multicast group is joined on the proper host interface. + /// + /// Used to identify application in the NormSession. + /// Name of the interface + /// A return value of true indicates success while a return value of false indicates that the specified interface was + /// invalid. This function will always return true if made before calls to NormStartSender() or NormStartReceiver(). + /// However, those calls may fail if an invalid interface was specified with the call described here. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetMulticastInterface(long sessionHandle, string interfaceName); + + /// + /// This function sets the source address for Source-Specific Multicast (SSM) operation. + /// + /// Used to identify application in the NormSession. + /// Address to be set as source for source-specific multicast operation. + /// A return value of true indicates success while a return value of false indicates that the specified source address + /// was invalid. Note that if a valid IP address is specified but is improper for SSM (e.g., an IP multicast address) the + /// later calls to NormStartSender() or NormStartReceiver() may fail. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetSSM(long sessionHandle, string sourceAddress); + + /// + /// This function specifies the time-to-live (ttl) for IP Multicast datagrams generated by NORM for the specified + /// sessionHandle. The IP TTL field limits the number of router "hops" that a generated multicast packet may traverse + /// before being dropped. + /// + /// Used to identify application in the NormSession. + /// If TTL is equal to one, the transmissions will be limited to the local area network + /// (LAN) of the host computers network interface. Larger TTL values should be specified to span large networks. + /// A return value of true indicates success while a return value of false indicates that the specified ttl could not + /// be set. This function will always return true if made before calls to NormStartSender() or NormStartReceiver(). + /// However, those calls may fail if the desired ttl value cannot be set. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetTTL(long sessionHandle, byte ttl); + + /// + /// This function specifies the type-of-service (tos) field value used in IP Multicast datagrams generated by NORM for the specified sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The IP TOS field value can be used as an indicator that a "flow" of packets may merit special + /// Quality-of-Service (QoS) treatment by network devices. + /// Users should refer to applicable QoS information for their network to determine the expected interpretation and + /// treatment (if any) of packets with explicit TOS marking. + /// A return value of true indicates success while a return value of false indicates that the specified tos could not + /// be set. This function will always return true if made before calls to NormStartSender() or NormStartReceiver(). + /// However, those calls may fail if the desired tos value cannot be set. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetTOS(long sessionHandle, byte tos); + + /// + /// This function enables or disables loopback operation for the indicated NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// If loopback is set to true, loopback operation is enabled which allows the application to receive its own message traffic. + /// Thus, an application which is both actively receiving and sending may receive its own transmissions. + /// A return value of true indicates success while a return value of false indicates that the loopback operation could not be set. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetLoopback(long sessionHandle, bool loopback); + + [DllImport(NORM_LIBRARY)] + public static extern void NormSetMessageTrace(long sessionHandle, bool flag); + + [DllImport(NORM_LIBRARY)] + public static extern void NormSetTxLoss(long sessionHandle, double precent); + + [DllImport(NORM_LIBRARY)] + public static extern void NormSetRxLoss(long sessionHandle, double precent); + + /// + /// This function allows NORM debug output to be directed to a file instead of the default STDERR. + /// + /// Used to identify application in the NormSession. + /// Full path and name of the debug log. + /// The function returns true on success. If the specified file cannot be opened a value of false is returned. + [DllImport(NORM_LIBRARY)] + public static extern bool NormOpenDebugLog(long instanceHandle, string path); + + /// + /// This function disables NORM debug output to be directed to a file instead of the default STDERR. + /// + /// Used to identify application in the NormSession. + /// The function returns true on success. + [DllImport(NORM_LIBRARY)] + public static extern bool NormCloseDebugLog(long instanceHandle); + + /// + /// This function allows NORM debug output to be directed to a named pipe. + /// + /// Used to identify application in the NormSession. + /// The debug pipe name. + /// The function returns true on success. + [DllImport(NORM_LIBRARY)] + public static extern bool NormOpenDebugPipe(long instanceHandle, string pipeName); + + /// + /// This function controls the verbosity of NORM debugging output. Higher values of level result in more detailed + /// output. The highest level of debugging is 12. The debug output consists of text written to STDOUT by default but + /// may be directed to a log file using the NormOpenDebugLog() function. + /// + /// + /// PROTOLIB DEBUG LEVELS: + /// PL_FATAL=0 - The FATAL level designates very severe error events that will presumably lead the application to abort. + /// PL_ERROR=1 - The ERROR level designates error events that might still allow the application to continue running. + /// PL_WARN=2 - The WARN level designates potentially harmful situations. + /// PL_INFO=3 - The INFO level designates informational messages that highlight the progress of the application at coarse-grained level. + /// PL_DEBUG=4 - The DEBUG level designates fine-grained informational events that are most useful to debug an application. + /// PL_TRACE=5 - The TRACE level designates finer-grained informational events than the DEBUG. + /// PL_DETAIL=6 - The TRACE level designates even finer-grained informational events than the DEBUG. + /// PL_MAX=7 - Turn all comments on. + /// PL_ALWAYS - Messages at this level are always printed regardless of debug level. + /// + [DllImport(NORM_LIBRARY)] + public static extern void NormSetDebugLevel(int level); + + /// + /// Returns the currently set debug level. + /// + /// Returns the currently set debug level. + [DllImport(NORM_LIBRARY)] + public static extern int NormGetDebugLevel(); + + [DllImport(NORM_LIBRARY)] + public static extern void NormSetReportInterval(long sessionHandle, double interval); + + [DllImport(NORM_LIBRARY)] + public static extern double NormGetReportInterval(long sessionHandle); + + [DllImport(NORM_LIBRARY)] + public static extern int NormGetRandomSessionId(); + + /// + /// The application's participation as a sender within a specified NormSession begins when this function is called. + /// + /// Valid NormSessionHandle previously obtained with a call to NormCreateSession() + /// Application-defined value used as the instance_id field of NORM sender messages for the application's participation within a session. + /// This specifies the maximum memory space (in bytes) the NORM protocol engine is allowed to use to buffer any sender calculated FEC segments and repair state for the session. + /// This parameter sets the maximum payload size (in bytes) of NORM sender messages (not including any NORM message header fields). + /// This parameter sets the number of source symbol segments (packets) per coding block, for the + /// systematic Reed-Solomon FEC code used in the current NORM implementation. + /// This parameter sets the maximum number of parity symbol segments (packets) the sender is willing to calculate per FEC coding block. + /// Sets the NormFecType. + /// A value of true is returned upon success and false upon failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormStartSender(long instanceHandle, int instanceId, long bufferSpace, int segmentSize, short numData, short numParity, NormFecType fecId); + + /// + /// This function terminates the application's participation in a NormSession as a sender. By default, the sender will + /// immediately exit the session identified by the sessionHandle parameter without notifying the receiver set of its intention. + /// + /// Used to identify application in the NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormStopSender(long sessionHandle); + + /// + /// This function sets the transmission rate (in bits per second (bps)) limit used for NormSender transmissions for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// Transmission rate. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetTxRate(long sessionHandle, double rate); + + /// + /// This function retrieves the current sender transmission rate in units of bits per second (bps) for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// This function returns the sender transmission rate in units of bits per second (bps). + [DllImport(NORM_LIBRARY)] + public static extern double NormGetTxRate(long sessionHandle); + + /// + /// This function can be used to set a non-default socket buffer size for the UDP socket used by the specified NORM sessionHandle for data transmission. + /// + /// Used to identify application in the NormSession. + /// The bufferSize parameter specifies the desired socket buffer size in bytes. + /// This function returns true upon success and false upon failure. Possible failure modes include an invalid sessionHandle parameter, + /// a call to NormStartReceiver() or NormStartSender() has not yet been made for the session, or an invalid bufferSize was given. + /// Note some operating systems may require additional system configuration to use non-standard socket buffer sizes. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetTxSocketBuffer(long sessionHandle, long bufferSize); + + /// + /// This function controls a scaling factor that is used for sender timer-based flow control for the the specified NORM + /// sessionHandle. Timer-based flow control works by preventing the NORM sender application from enqueueing + /// new transmit objects or stream data that would purge "old" objects or stream data when there has been recent + /// NACK activity for those old objects or data. + /// + /// Used to identify application in the NormSession. + /// The flowControlFactor is used to compute a delay time for when a sender buffered object (or block of stream + /// data) may be released (i.e. purged) after transmission or applicable NACKs reception. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetFlowControl(long sessionHandle, double flowControlFactor); + + /// + /// This function enables (or disables) the NORM sender congestion control operation for the session designated by + /// the sessionHandle parameter. For best operation, this function should be called before the call to NormStartSender() is made, but congestion control operation can be dynamically enabled/disabled during the course + /// of sender operation. + /// + /// Used to identify application in the NormSession. + /// Specifies whether to enable or disable the NORM sender congestion control operation. + /// The rate set by NormSetTxRate() has no effect when congestion control operation is enabled, unless the adjustRate + /// parameter here is set to false. When the adjustRate parameter is set to false, the NORM Congestion Control + /// operates as usual, with feedback collected from the receiver set and the "current limiting receiver" identified, except + /// that no actual adjustment is made to the sender's transmission rate. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetCongestionControl(long sessionHandle, bool enable, bool adjustRate); + + /// + /// This function sets the range of sender transmission rates within which the NORM congestion control algorithm is + /// allowed to operate for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// rateMin corresponds to the minimum transmission rate (bps). + /// rateMax corresponds to the maximum transmission rate (bps). + [DllImport(NORM_LIBRARY)] + public static extern void NormSetTxRateBounds(long sessionHandle, double rateMin, double rateMax); + + /// + /// This function sets limits that define the number and total size of pending transmit objects a NORM sender will allow to be enqueued by the application. + /// + /// Used to identify application in the NormSession. + /// The sizeMax parameter sets the maximum total size, in bytes, of enqueued objects allowed. + /// The countMin parameter sets the minimum number of objects the application may enqueue, + /// regardless of the objects' sizes and the sizeMax value. + /// The countMax parameter sets a ceiling on how many objects may be enqueued, + /// regardless of their total sizes with respect to the sizeMax setting. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetTxCacheBounds(long sessionHandle, long sizeMax, long countMin, long countMax); + + /// + /// This function sets the quantity of proactive "auto parity" NORM_DATA messages sent at the end of each FEC coding + /// block. By default (i.e., autoParity = 0), FEC content is sent only in response to repair requests (NACKs) from receivers. + /// + /// Used to identify application in the NormSession. + /// Setting a non-zero value for autoParity, the sender can automatically accompany each coding + /// block of transport object source data segments ((NORM_DATA messages) with the set number of FEC segments. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetAutoParity(long sesssionHandle, short autoParity); + + /// + /// This function sets the sender's estimate of group round-trip time (GRTT) (in units of seconds) for the given NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// group round-trip time + [DllImport(NORM_LIBRARY)] + public static extern void NormSetGrttEstimate(long sessionHandle, double grtt); + + /// + /// This function returns the sender's current estimate(in seconds) of group round-trip timing (GRTT) for the given NORM session. + /// + /// Used to identify application in the NormSession. + /// This function returns the current sender group round-trip timing (GRTT) estimate (in units of seconds). + /// A value of -1.0 is returned if an invalid session value is provided. + [DllImport(NORM_LIBRARY)] + public static extern double NormGetGrttEstimate(long sessionHandle); + + /// + /// This function sets the sender's maximum advertised GRTT value for the given NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The grttMax parameter, in units of seconds, limits the GRTT used by the group for scaling protocol timers, regardless + /// of larger measured round trip times. The default maximum for the NRL NORM library is 10 seconds. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetGrttMax(long sessionHandle, double grttMax); + + /// + /// This function sets the sender's mode of probing for round trip timing measurement responses from the receiver set for the given NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// Possible values for the probingMode parameter include NORM_PROBE_NONE, NORM_PROBE_PASSIVE, and NORM_PROBE_ACTIVE. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetGrttProbingMode(long sesssionHandle, NormProbingMode probingMode); + + /// + /// This function controls the sender GRTT measurement and estimation process for the given NORM sessionHandle. + /// The NORM sender multiplexes periodic transmission of NORM_CMD(CC) messages with its ongoing data transmission + /// or when data transmission is idle.When NORM congestion control operation is enabled, these probes are sent + /// once per RTT of the current limiting receiver(with respect to congestion control rate). In this case the intervalMin + /// and intervalMax parameters (in units of seconds) control the rate at which the sender's estimate of GRTT is updated. + /// + /// Used to identify application in the NormSession. + /// At session start, the estimate is updated at intervalMin and the update interval time is doubled until intervalMax is reached. + /// At session start, the estimate is updated at intervalMin and the update interval time is doubled until intervalMax is reached. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetGrttProbingInterval(long sessionHandle, double intervalMin, double intervalMax); + + /// + /// This function sets the sender's "backoff factor" for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The backoffFactor (in units of seconds) is used to scale various timeouts related to the NACK repair process. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetBackoffFactor(long sessionHandle, double backoffFactor); + + /// + /// This function sets the sender's estimate of receiver group size for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The sender advertises its groupSize setting to the receiver group in NORM protocol message + /// headers that, in turn, use this information to shape the distribution curve of their random timeouts for the timer-based, + /// probabilistic feedback suppression technique used in the NORM protocol. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetGroupSize(long sessionHandle, long groupSize); + + /// + /// This routine sets the "robustness factor" used for various NORM sender functions. These functions include the + /// number of repetitions of "robustly-transmitted" NORM sender commands such as NORM_CMD(FLUSH) or similar + /// application-defined commands, and the number of attempts that are made to collect positive acknowledgement + /// from receivers.These commands are distinct from the NORM reliable data transmission process, but play a role + /// in overall NORM protocol operation. + /// + /// Used to identify application in the NormSession. + /// The default txRobustFactor value is 20. This relatively large value makes + /// the NORM sender end-of-transmission flushing and positive acknowledgement collection functions somewhat immune from packet loss. + /// Setting txRobustFactor to a value of -1 makes the redundant transmission of these commands continue indefinitely until completion. + /// + [DllImport(NORM_LIBRARY)] + public static extern void NormSetTxRobustFactor(long sessionHandle, int txRobustFactor); + + /// + /// This function enqueues a file for transmission within the specified NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The fileName parameter specifies the path to the file to be transmitted. The NORM protocol engine + /// read and writes directly from/to file system storage for file transport, potentially providing for a very large virtual + /// "repair window" as needed for some applications. While relative paths with respect to the "current working directory" + /// may be used, it is recommended that full paths be used when possible. + /// The optional infoPtr and infoLen parameters + /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen + /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the + /// NORM_INFO content is left to the application's discretion. + /// The optional infoPtr and infoLen parameters + /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen + /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the + /// NORM_INFO content is left to the application's discretion + /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. + [DllImport(NORM_LIBRARY)] + public static extern long NormFileEnqueue(long sessionHandle, string fileName, byte[]? infoPtr, int infoLen); + + /// + /// This function enqueues a segment of application memory space for transmission within the specified NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The dataPtr parameter must be a valid pointer to the area of application memory to be transmitted. + /// The dataLen parameter indicates the quantity of data to transmit. + /// The optional infoPtr and infoLen parameters + /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen + /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the + /// NORM_INFO content is left to the application's discretion. + /// The optional infoPtr and infoLen parameters + /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen + /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the + /// NORM_INFO content is left to the application's discretion + /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. + [DllImport(NORM_LIBRARY)] + public static extern long NormDataEnqueue(long sessionHandle, byte[] dataPtr, int dataLen, byte[]? infoPtr, int infoLen); + + /// + /// This function allows the application to resend (or reset transmission of) a NORM_OBJECT_FILE or NORM_OBJECT_DATA + /// transmit object that was previously enqueued for the indicated sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The objectHandle parameter must be a valid transmit NormObjectHandle that has not yet been "purged" + /// from the sender's transmit queue. + /// A value of true is returned upon success and a value of false is returned upon failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormRequeueObject(long sessionHandle, long objectHandle); + + /// + /// This function opens a NORM_OBJECT_STREAM sender object and enqueues it for transmission within the indicated sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The bufferSize parameter controls the size of the stream's "repair window" + /// which limits how far back the sender will "rewind" to satisfy receiver repair requests. + /// Note that no data is sent until subsequent calls to NormStreamWrite() are made unless + /// NORM_INFO content is specified for the stream with the infoPtr and infoLen parameters. Example usage of + /// NORM_INFO content for NORM_OBJECT_STREAM might include application-defined data typing or other information + /// which will enable NORM receiver applications to properly interpret the received stream as it is being received. + /// Note that no data is sent until subsequent calls to NormStreamWrite() are made unless + /// NORM_INFO content is specified for the stream with the infoPtr and infoLen parameters. Example usage of + /// NORM_INFO content for NORM_OBJECT_STREAM might include application-defined data typing or other information + /// which will enable NORM receiver applications to properly interpret the received stream as it is being received. + /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. + [DllImport(NORM_LIBRARY)] + public static extern long NormStreamOpen(long sessionHandle, long bufferSize, byte[]? infoPtr, int infoLen); + + /// + /// This function halts transfer of the stream specified by the streamHandle parameter and releases any resources + /// used unless the associated object has been explicitly retained by a call to NormObjectRetain(). + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + /// The optional graceful parameter, when + /// set to a value of true, may be used by NORM senders to initiate "graceful" shutdown of a transmit stream. + [DllImport(NORM_LIBRARY)] + public static extern void NormStreamClose(long streamHandle, bool graceful); + + /// + /// This function enqueues data for transmission within the NORM stream specified by the streamHandle parameter. + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + /// The buffer parameter must be a pointer to the data to be enqueued. + /// The numBytes parameter indicates the length of the data content. + /// This function returns the number of bytes of data successfully enqueued for NORM stream transmission. + [DllImport(NORM_LIBRARY)] + internal static extern int NormStreamWrite(long streamHandle, byte[] buffer, int numBytes); + + /// + /// This function causes an immediate "flush" of the transmit stream specified by the streamHandle parameter. + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + /// The optional eom parameter, when set to true, allows the sender application to mark an end-of-message indication + /// (see NormStreamMarkEom()) for the stream and initiate flushing in a single function call. + /// The default stream "flush" operation invoked via + /// NormStreamFlush() for flushMode equal to NORM_FLUSH_PASSIVE causes NORM to immediately transmit all + /// enqueued data for the stream(subject to session transmit rate limits), even if this results in NORM_DATA messages + /// with "small" payloads.If the optional flushMode parameter is set to NORM_FLUSH_ACTIVE, the application can + /// achieve reliable delivery of stream content up to the current write position in an even more proactive fashion.In + /// this case, the sender additionally, actively transmits NORM_CMD(FLUSH) messages after any enqueued stream + /// content has been sent. This immediately prompt receivers for repair requests which reduces latency of reliable + /// delivery, but at a cost of some additional messaging. Note any such "active" flush activity will be terminated upon + /// the next subsequent write to the stream.If flushMode is set to NORM_FLUSH_NONE, this call has no effect other than + /// the optional end-of-message marking described here + [DllImport(NORM_LIBRARY)] + public static extern void NormStreamFlush(long streamHandle, bool eom, NormFlushMode flushMode); + + /// + /// This function sets "automated flushing" for the NORM transmit stream indicated by the streamHandle parameter. + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + /// Possible values for the flushMode parameter include NORM_FLUSH_NONE, NORM_FLUSH_PASSIVE, and NORM_FLUSH_ACTIVE. + [DllImport(NORM_LIBRARY)] + public static extern void NormStreamSetAutoFlush(long streamHandle, NormFlushMode flushMode); + + /// + /// This function controls how the NORM API behaves when the application attempts to enqueue new stream data + /// for transmission when the associated stream's transmit buffer is fully occupied with data pending original or repair + /// transmission. + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + /// By default (pushEnable = false), a call to NormStreamWrite() will return a zero value under this + /// condition, indicating it was unable to enqueue the new data. However, if pushEnable is set to true for a given + /// streamHandle, the NORM protocol engine will discard the oldest buffered stream data(even if it is pending repair + /// transmission or has never been transmitted) as needed to enqueue the new data. + [DllImport(NORM_LIBRARY)] + public static extern void NormStreamSetPushEnable(long streamHandle, bool pushEnable); + + /// + /// This function can be used to query whether the transmit stream, specified by the streamHandle parameter, has + /// buffer space available so that the application may successfully make a call to NormStreamWrite(). + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + /// This function returns a value of true when there is transmit buffer space to which the application may write and false otherwise. + [DllImport(NORM_LIBRARY)] + public static extern bool NormStreamHasVacancy(long streamHandle); + + /// + /// This function allows the application to indicate to the NORM protocol engine that the last data successfully written + /// to the stream indicated by streamHandle corresponded to the end of an application-defined message boundary. + /// + /// The streamHandle parameter must be a valid transmit NormObjectHandle. + [DllImport(NORM_LIBRARY)] + public static extern void NormStreamMarkEom(long streamHandle); + + /// + /// This function specifies a "watermark" transmission point at which NORM sender protocol operation should perform + /// a flushing process and/or positive acknowledgment collection for a given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The objectHandle parameter must be a valid transmit NormObjectHandle that has not yet been "purged" from the sender's transmit queue. + /// The optional overrideFlush parameter, when set to true, causes the watermark acknowledgment process that is + /// established with this function call to potentially fully supersede the usual NORM end-of-transmission flushing + /// process that occurs.If overrideFlush is set and the "watermark" transmission point corresponds to the last + /// transmission that will result from data enqueued by the sending application, then the watermark flush completion + /// will terminate the usual flushing process + /// The function returns true upon successful establishment of the watermark point. The function may return false upon failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetWatermark(long sessionHandle, long objectHandle, bool overrideFlush); + + [DllImport(NORM_LIBRARY)] + public static extern bool NormResetWatermark(long sessionHandle); + + /// + /// This function cancels any "watermark" acknowledgement request that was previously set via the NormSetWatermark() function for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormCancelWatermark(long sessionHandle); + + /// + /// When this function is called, the specified nodeId is added to the list of NormNodeId values (i.e., the "acking node" + /// list) used when NORM sender operation performs positive acknowledgement (ACK) collection for the specified sessionHandle. + /// + /// Used to identify application in the NormSession. + /// Identifies the application's presence in the NormSession. + /// The function returns true upon success and false upon failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormAddAckingNode(long sessionHandle, long nodeId); + + /// + /// This function deletes the specified nodeId from the list of NormNodeId values used when NORM sender operation + /// performs positive acknowledgement (ACK) collection for the specified sessionHandle. + /// + /// Used to identify application in the NormSession. + /// Identifies the application's presence in the NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormRemoveAckingNode(long sessionHandle, long nodeId); + + /// + /// This function queries the status of the watermark flushing process and/or positive acknowledgment collection + /// initiated by a prior call to NormSetWatermark() for the given sessionHandle. + /// + /// Used to identify application in the NormSession. + /// Identifies the application's presence in the NormSession. + /// + /// Possible return values include: + /// NORM_ACK_INVALID - The given sessionHandle is invalid or the given nodeId is not in the sender's acking list. + /// NORM_ACK_FAILURE - The positive acknowledgement collection process did not receive acknowledgment from every listed receiver (nodeId = NORM_NODE_ANY) or the identified nodeId did not respond. + /// NORM_ACK_PENDING - The flushing process at large has not yet completed (nodeId = NORM_NODE_ANY) or the given individual nodeId is still being queried for response. + /// NORM_ACK_SUCCESS - All receivers (nodeId = NORM_NODE_ANY) responded with positive acknowledgement or the given specific nodeId did acknowledge. + /// + [DllImport(NORM_LIBRARY)] + public static extern NormAckingStatus NormGetAckingStatus(long sessionHandle, long nodeId); + + /// + /// This function enqueues a NORM application-defined command for transmission. + /// + /// Used to identify application in the NormSession. + /// The cmdBuffer parameter points to a buffer containing the application-defined command content + /// that will be contained in the NORM_CMD(APPLICATION) message payload. + /// The cmdLength indicates the length of this content (in bytes) and MUST be less than or equal + /// to the segmentLength value for the given session (see NormStartSender()). + /// The command is NOT delivered reliably, but can be optionally transmitted with repetition + /// (once per GRTT) according to the NORM transmit robust factor value (see NormSetTxRobustFactor()) for the given + /// session if the robust parameter is set to true. + /// The function returns true upon success. The function may fail, returning false, if the session is not set for sender + /// operation (see NormStartSender()), the cmdLength exceeds the configured session segmentLength, or a previously- + /// enqueued command has not yet been sent. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSendCommand(long sessionHandle, byte[] cmdBuffer, int cmdLength, bool robust); + + /// + /// This function terminates any pending NORM_CMD(APPLICATION) transmission that was previously initiated with the NormSendCommand() call. + /// + /// Used to identify application in the NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormCancelCommand(long sessionHandle); + + /// + /// This function initiates the application's participation as a receiver within the NormSession identified by the sessionHandle parameter. + /// + /// Used to identify application in the NormSession. + /// The bufferSpace parameter is used to set a limit on the amount of bufferSpace allocated + /// by the receiver per active NormSender within the session. + /// A value of true is returned upon success and false upon failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormStartReceiver(long sessionHandle, long bufferSpace); + + /// + /// This function ends the application's participation as a receiver in the NormSession specified by the session parameter. + /// + /// Used to identify application in the NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormStopReceiver(long sessionHandle); + + /// + /// This function sets a limit on the number of outstanding (pending) NormObjects for which a receiver will keep state on a per-sender basis. + /// + /// Used to identify application in the NormSession. + /// Note that the value countMax sets a limit on the maximum consecutive range of objects that can be pending + [DllImport(NORM_LIBRARY)] + public static extern void NormSetRxCacheLimit(long sessionHandle, int countMax); + + /// + /// This function allows the application to set an alternative, non-default buffer size for the UDP socket used by the specified NORM sessionHandle for packet reception. + /// + /// Used to identify application in the NormSession. + /// The bufferSize parameter specifies the socket buffer size in bytes. + /// This function returns true upon success and false upon failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormSetRxSocketBuffer(long sessionHandle, long bufferSize); + + /// + /// This function provides the option to configure a NORM receiver application as a "silent receiver". This mode of + /// receiver operation dictates that the host does not generate any protocol messages while operating as a receiver + /// within the specified sessionHandle. + /// + /// Used to identify application in the NormSession. + /// SSetting the silent parameter to true enables silent receiver operation while + /// setting it to false results in normal protocol operation where feedback is provided as needed for reliability and + /// protocol operation. + /// When the maxDelay parameter is set to a non-negative value, the value determines the maximum number + /// of FEC coding blocks (according to a NORM sender's current transmit position) the receiver will cache an incompletely-received + /// FEC block before giving the application the (incomplete) set of received source segments. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetSilentReceiver(long sessionHandle, bool silent, int maxDelay); + + /// + /// This function controls the default behavior determining the destination of receiver feedback messages generated + /// while participating in the session. + /// + /// Used to identify application in the NormSession. + /// If the unicastNacks parameter is true, "unicast NACKing" is enabled for new remote + /// senders while it is disabled for state equal to false. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetDefaultUnicastNack(long sessionHandle, bool unicastNacks); + + /// + /// This function controls the destination address of receiver feedback messages generated in response to a specific + /// remote NORM sender corresponding to the remoteSender parameter. + /// + /// Used to specify the remote NORM sender. + /// If unicastNacks is true, "unicast NACKing" is enabled + /// while it is disabled for enable equal to false. + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeSetUnicastNack(long remoteSender, bool unicastNacks); + + /// + /// This function sets the default "synchronization policy" used when beginning (or restarting) reception of objects + /// from a remote sender (i.e., "syncing" to the sender) for the given sessionHandle + /// + /// Used to identify application in the NormSession. + /// The "synchronization policy" + /// is the behavior observed by the receiver with regards to what objects it attempts to reliably receive (via transmissions + /// of Negative Acknowledgements to the sender(s) or group as needed). There are currently two synchronization policy types defined. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetDefaultSyncPolicy(long sessionHandle, NormSyncPolicy syncPolicy); + + /// + /// This function sets the default "nacking mode" used when receiving objects for the given sessionHandle. + /// This allows the receiver application some control of its degree of participation in the repair process. By limiting receivers + /// to only request repair of objects in which they are really interested in receiving, some overall savings in unnecessary + /// network loading might be realized for some applications and users. + /// + /// Used to identify application in the NormSession. + /// Specifies the nacking mode. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetDefaultNackingMode(long sessionHandle, NormNackingMode nackingMode); + + /// + /// This function sets the default "nacking mode" used for receiving new objects from a specific sender as identified + /// by the remoteSender parameter. + /// + /// Used to specify the remote NORM sender. + /// Specifies the nacking mode. + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeSetNackingMode(long remoteSender, NormNackingMode nackingMode); + + /// + /// This function sets the "nacking mode" used for receiving a specific transport object as identified by the objectHandle parameter. + /// + /// Specifies the transport object. + /// Specifies the nacking mode. + [DllImport(NORM_LIBRARY)] + public static extern void NormObjectSetNackingMode(long objectHandle, NormNackingMode nackingMode); + + /// + /// This function allows the receiver application to customize, for a given sessionHandle, at what points the receiver + /// initiates the NORM NACK repair process during protocol operation. + /// + /// Used to identify application in the NormSession. + /// Specifies the repair boundary. + [DllImport(NORM_LIBRARY)] + public static extern void NormSetDefaultRepairBoundary(long sessionHandle, NormRepairBoundary repairBoundary); + + /// + /// This function allows the receiver application to customize, for the specific remote sender referenced by the remoteSender + /// parameter, at what points the receiver initiates the NORM NACK repair process during protocol operation. + /// + /// Used to specify the remote NORM sender. + /// Specifies the repair boundary. + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeSetRepairBoundary(long remoteSender, NormRepairBoundary repairBoundary); + + /// + /// This routine controls how persistently NORM receivers will maintain state for sender(s) and continue to request + /// repairs from the sender(s) even when packet reception has ceased. + /// + /// Used to identify application in the NormSession. + /// The robustFactor value determines how + /// many times a NORM receiver will self-initiate NACKing (repair requests) upon cessation of packet reception from + /// a sender. The default value is 20. Setting rxRobustFactor to -1 will make the NORM receiver infinitely persistent + /// (i.e., it will continue to NACK indefinitely as long as it is missing data content). + [DllImport(NORM_LIBRARY)] + public static extern void NormSetDefaultRxRobustFactor(long sessionHandle, int robustFactor); + + /// + /// This routine sets the robustFactor as described in NormSetDefaultRxRobustFactor() for an individual remote + /// sender identified by the remoteSender parameter. + /// + /// Used to specify the remote NORM sender. + /// The robustFactor value determines how + /// many times a NORM receiver will self-initiate NACKing (repair requests) upon cessation of packet reception from + /// a sender. The default value is 20. Setting rxRobustFactor to -1 will make the NORM receiver infinitely persistent + /// (i.e., it will continue to NACK indefinitely as long as it is missing data content). + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeSetRxRobustFactor(long remoteSender, int robustFactor); + + /// + /// This function can be used by the receiver application to read any available data from an incoming NORM stream. + /// + /// The streamHandle parameter here must correspond to a valid NormObjectHandle value provided during such a + /// prior NORM_RX_OBJECT_NEW notification. + /// The buffer parameter must be a pointer to an array where the received + /// data can be stored of a length as referenced by the numBytes pointer + /// Specifies the length of data. + /// + [DllImport(NORM_LIBRARY)] + public static extern bool NormStreamRead(long streamHandle, byte[] buffer, ref int numBytes); + + /// + /// This function advances the read offset of the receive stream referenced by the streamHandle parameter to align + /// with the next available message boundary + /// + /// The streamHandle parameter here must correspond to a valid NormObjectHandle value provided during such a + /// prior NORM_RX_OBJECT_NEW notification. + /// This function returns a value of true when start-of-message is found. The next call to NormStreamRead() will + /// retrieve data aligned with the message start. If no new message boundary is found in the buffered receive data for + /// the stream, the function returns a value of false. In this case, the application should defer repeating a call to this + /// function until a subsequent NORM_RX_OBJECT_UPDATE notification is posted. + [DllImport(NORM_LIBRARY)] + public static extern bool NormStreamSeekMsgStart(long streamHandle); + + /// + /// This function retrieves the current read offset value for the receive stream indicated by the streamHandle parameter. + /// + /// The streamHandle parameter here must correspond to a valid NormObjectHandle value provided during such a + /// prior NORM_RX_OBJECT_NEW notification. + /// This function returns the current read offset in bytes. The return value is undefined for sender streams. There is + /// no error result. + [DllImport(NORM_LIBRARY)] + public static extern long NormStreamGetReadOffset(long streamHandle); + + /// + /// This function can be used to determine the object type (NORM_OBJECT_DATA, NORM_OBJECT_FILE, or NORM_OBJECT_STREAM) for the + /// NORM transport object identified by the objectHandle parameter. + /// + /// The objectHandle must refer to a current, valid transport object. + /// This function returns the NORM object type. Valid NORM object types include NORM_OBJECT_DATA, NORM_OBJECT_FILE, + /// or NORM_OBJECT_STREAM. A type value of NORM_OBJECT_NONE will be returned for an objectHandle value of NORM_OBJECT_INVALID. + [DllImport(NORM_LIBRARY)] + public static extern NormObjectType NormObjectGetType(long objectHandle); + + /// + /// This function can be used to determine if the sender has associated any NORM_INFO content with the transport object + /// specified by the objectHandle parameter. + /// + /// The objectHandle must refer to a current, valid transport object. + /// A value of true is returned if NORM_INFO is (or will be) available for the specified transport object. A value of + /// false is returned otherwise. + [DllImport(NORM_LIBRARY)] + public static extern bool NormObjectHasInfo(long objectHandle); + + /// + /// This function can be used to determine the length of currently available NORM_INFO content (if any) associated + /// with the transport object referenced by the objectHandle parameter. + /// + /// The objectHandle must refer to a current, valid transport object. + /// The length of the NORM_INFO content, in bytes, of currently available for the specified transport object is returned. + /// A value of 0 is returned if no NORM_INFO content is currently available or associated with the object. + [DllImport(NORM_LIBRARY)] + public static extern int NormObjectGetInfoLength(long objectHandle); + + /// + /// This function copies any NORM_INFO content associated (by the sender application) with the transport object specified + /// by objectHandle into the provided memory space referenced by the buffer parameter. + /// + /// The objectHandle must refer to a current, valid transport object. + /// Provided memory space for the object info. + /// The bufferLen parameter indicates the length of the buffer space in bytes. + /// The actual length of currently available NORM_INFO content for the specified transport object is returned. This + /// function can be used to determine the length of NORM_INFO content for the object even if a NULL buffer value and + /// zero bufferLen is provided. A zero value is returned if NORM_INFO content has not yet been received (or is nonexistent) for the specified object. + [DllImport(NORM_LIBRARY)] + public static extern int NormObjectGetInfo(long objectHandle, [Out] byte[] buffer, int bufferLen); + + /// + /// This function can be used to determine the size (in bytes) of the transport object specified by the objectHandle parameter. + /// + /// The objectHandle must refer to a current, valid transport object. + /// A size of the data content of the specified object, in bytes, is returned. Note that it may be possible that some objects + /// have zero data content, but do have NORM_INFO content available. + [DllImport(NORM_LIBRARY)] + public static extern int NormObjectGetSize(long objectHandle); + + /// + /// This function can be used to determine the progress of reception of the NORM transport object identified by the objectHandle parameter + /// + /// The objectHandle must refer to a current, valid transport object. + /// A number of object source data bytes pending reception (or transmission) is returned. + [DllImport(NORM_LIBRARY)] + public static extern long NormObjectGetBytesPending(long objectHandle); + + /// + /// This function immediately cancels the transmission of a local sender transport object or the reception of a specified + /// object from a remote sender as specified by the objectHandle parameter + /// + /// The objectHandle must refer to a current, valid transport object. + [DllImport(NORM_LIBRARY)] + public static extern void NormObjectCancel(long objectHandle); + + /// + /// This function "retains" the objectHandle and any state associated with it for further use by the application even + /// when the NORM protocol engine may no longer require access to the associated transport object. + /// + /// The objectHandle must refer to a current, valid transport object. + [DllImport(NORM_LIBRARY)] + public static extern void NormObjectRetain(long objectHandle); + + /// + /// This function complements the NormObjectRetain() call by immediately freeing any resources associated with + /// the given objectHandle, assuming the underlying NORM protocol engine no longer requires access to the corresponding + /// transport object. Note the NORM protocol engine retains/releases state for associated objects for its own + /// needs and thus it is very unsafe for an application to call NormObjectRelease() for an objectHandle for which + /// it has not previously explicitly retained via NormObjectRetain(). + /// + /// The objectHandle must refer to a current, valid transport object. + [DllImport(NORM_LIBRARY)] + public static extern void NormObjectRelease(long objectHandle); + + /// + /// This function copies the name, as a NULL-terminated string, of the file object specified by the objectHandle + /// parameter into the nameBuffer of length bufferLen bytes provided by the application. + /// + /// This type is used to reference state kept for data transport objects being actively transmitted or received. The objectHandle parameter + /// must refer to a valid NormObjectHandle for an object of type NORM_OBJECT_FILE. + /// provided memory space for the name of the file + /// idicates the length of the nameBuffer + /// + /// This function returns true upon success and false upon failure. Possible failure conditions include the objectHandle + /// does not refer to an object of type NORM_OBJECT_FILE. + /// + [DllImport(NORM_LIBRARY)] + public static extern bool NormFileGetName(long fileHandle, [Out] char[] nameBuffer, int bufferLen); + + /// + /// This function renames the file used to store content for the NORM_OBJECT_FILE transport object specified by + /// the objectHandle parameter. This allows receiver applications to rename (or move) received files as needed.NORM + /// uses temporary file names for received files until the application explicitly renames the file.For example, sender + /// applications may choose to use the NORM_INFO content associated with a file object to provide name and/or typing + /// information to receivers. + /// + /// This type is used to reference state kept for data transport objects being actively transmitted or received. + /// parameter must be a NULL-terminated string which should specify the full desired path name to be used + /// + /// This function returns true upon success and false upon failure. Possible failure conditions include the case where + /// the objectHandle does not refer to an object of type NORM_OBJECT_FILE and where NORM was unable to successfully + /// create any needed directories and/or the file itself. + /// + [DllImport(NORM_LIBRARY)] + public static extern bool NormFileRename(long fileHandle, string fileName); + + /// + /// This function allows the application to access the data storage area associated with a transport object of type + /// NORM_OBJECT_DATA.For example, the application may use this function to copy the received data content for its + /// own use.Alternatively, the application may establish "ownership" for the allocated memory space using the + /// NormDataDetachData() function if it is desired to avoid the copy. + /// + /// This type is used to reference state kept for data transport objects being actively transmitted or received. + /// + /// This function returns a pointer to the data storage area for the specified transport object. A NULL value may be + /// returned if the object has no associated data content or is not of type NORM_OBJECT_DATA. + /// + [DllImport(NORM_LIBRARY)] + public static extern nint NormDataAccessData(long objectHandle); + + /// + /// This function retrieves the NormNodeHandle corresponding to the remote sender of the transport object associated with the given objectHandle parameter. + /// + /// This type is used to reference state kept for data transport objects being actively transmitted or received. + /// This function returns the NormNodeHandle corresponding to the remote sender of the transport object associated with the given objectHandle parameter. + /// A value of NORM_NODE_INVALID is returned if the specified objectHandle + /// references a locally originated, sender object. + [DllImport(NORM_LIBRARY)] + public static extern long NormObjectGetSender(long objectHandle); + + /// + /// This function retrieves the NormNodeId identifier for the remote participant referenced by the given nodeHandle value. + /// + /// This type is used to reference state kept by the NORM implementation with respect to other participants within a NormSession. + /// This function returns the NormNodeId value associated with the specified nodeHandle. + /// In the case nodeHandle is equal to NORM_NODE_INVALID, the return value will be NORM_NODE_NONE. + [DllImport(NORM_LIBRARY)] + public static extern long NormNodeGetId(long nodeHandle); + + /// + /// This function retrieves the current network source address detected for packets received from remote NORM sender referenced by the nodeHandle parameter. + /// + /// This type is used to reference state kept by the NORM implementation with respect to other participants within a NormSession. + /// The addrBuffer must be a pointer to storage of bufferLen bytes in length in which the referenced sender node's address will be returned + /// A return value of false indicates that either no command was available or the provided buffer size + /// port number and/or specify a specific source address binding that is used for packet transmission. + /// A value of true is returned upon success and false upon failure. An invalid nodeHandle parameter value would lead to such failure. + [DllImport(NORM_LIBRARY)] + public static extern bool NormNodeGetAddress(long nodeHandle, [Out] byte[] addrBuffer, ref int bufferLen, out int port); + + /// + /// This function retrieves the advertised estimate of group round-trip timing (GRTT) for the remote sender referenced by the given nodeHandle value. + /// Newly-starting senders that have been participating as a receiver within a group + /// may wish to use this function to provide a more accurate startup estimate of GRTT prior to a call to NormStartSender() + /// + /// This type is used to reference state kept by the NORM implementation with respect to other participants within a NormSession. + /// This function returns the remote sender's advertised GRTT estimate in units of seconds. + /// A value of -1.0 is returned upon failure.An invalid nodeHandle parameter value will lead to such failure. + [DllImport(NORM_LIBRARY)] + public static extern double NormNodeGetGrtt(long nodeHandle); + + /// + /// This function retrieves the content of an application-defined command that was received from a remote sender associated with the given nodeHandle. + /// + /// notification for a given remote sender when multiple senders may be providing content + /// Allocated system resources for each active sender + /// A return value of false indicates that either no command was available or the provided buffer size + /// This function returns true upon successful retrieval of command content. A return value of false indicates that + /// either no command was available or the provided buffer size (buflen parameter) was inadequate. + /// The value referenced by the buflen parameter is adjusted to indicate the actual command length (in bytes) upon return. + [DllImport(NORM_LIBRARY)] + public static extern bool NormNodeGetCommand(long remoteSender, [Out] byte[] buffer, ref int buflen); + + /// + /// This function releases memory resources that were allocated for a remote sender. + /// + /// notification for a given remote sender when multiple senders may be providing content + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeFreeBuffers(long remoteSender); + + /// + /// this function allows the application to retain state associated with a given nodeHandle + /// value even when the underlying NORM protocol engine might normally + /// free the associated state and thus invalidate the NormNodeHandle. + /// + /// This type is used to reference state kept by the NORM implementation with respect to other participants within a NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeRetain(long nodeHandle); + + /// + /// In complement to the NormNodeRetain() function, this API call releases the specified nodeHandle so that the + /// NORM protocol engine may free associated resources as needed.Once this call is made, the application should + /// no longer reference the specified NormNodeHandle, unless it is still valid. + /// + /// This type is used to reference state kept by the NORM implementation with respect to other participants within a NormSession. + [DllImport(NORM_LIBRARY)] + public static extern void NormNodeRelease(long nodeHandle); + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs new file mode 100644 index 00000000..bc55bc7f --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs @@ -0,0 +1,39 @@ +using System.Runtime.InteropServices; + +namespace Mil.Navy.Nrl.Norm +{ + /// + /// A transport object of type NORM_OBJECT_FILE. + /// + /// + /// The data storage area for the specified transport object. + /// + public class NormData : NormObject + { + /// + /// The data storage area associated with a transport object of type NORM_OBJECT_DATA. + /// + public byte[] Data + { + get + { + var dataPointer = NormDataAccessData(_handle); + var length = NormObjectGetSize(_handle); + var data = new byte[length]; + for (var i = 0; i < length; i++) + { + data[i] = Marshal.ReadByte(dataPointer, i); + } + return data; + } + } + + /// + /// Constructor of NormData + /// + /// Type is used to reference state kept for data transport objects being actively transmitted or received. + internal NormData(long handle) : base(handle) + { + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormEvent.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormEvent.cs new file mode 100644 index 00000000..866fdf87 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormEvent.cs @@ -0,0 +1,112 @@ +namespace Mil.Navy.Nrl.Norm +{ + /// + /// The NormEvent type is used to describe significant NORM protocol events. + /// + public class NormEvent + { + /// + /// The type identifies the event with one of NORM protocol events. + /// + private NormEventType _type { get; } + /// + /// Used to identify application in the NormSession. + /// + private long _sessionHandle { get; } + /// + /// This type is used to reference state kept by the NORM implementation with respect to other participants within a NormSession. + /// + private long _nodeHandle { get; } + /// + /// This type is used to reference state kept for data transport objects being actively transmitted or received. + /// + private long _objectHandle { get; } + + /// + /// The Parameterized constructor of NormEvent + /// + /// indicates the NormEventType and determines how the other fields should be interpreted. + /// indicates the applicable NormSessionHandle to which the event applies. + /// indicates the applicable NormNodeHandle to which the event applies + /// indicates the applicable NormObjectHandle to which the event applies. + public NormEvent(NormEventType type, long sessionHandle, long nodeHandle, long objectHandle) + { + _type = type; + _sessionHandle = sessionHandle; + _nodeHandle = nodeHandle; + _objectHandle = objectHandle; + } + + /// + /// The type identifies the event with one of NORM protocol events. + /// + public NormEventType Type => _type; + + /// + /// The NormSession associated with the event. + /// + public NormSession? Session + { + get + { + if (_sessionHandle == NormSession.NORM_SESSION_INVALID) + { + return null; + } + return NormSession.GetSession(_sessionHandle); + } + } + + /// + /// A remote participant associated with the event. + /// + public NormNode? Node + { + get + { + if (_nodeHandle == 0) + { + return null; + } + return new NormNode(_nodeHandle); + } + } + + /// + /// NORM transport object. + /// + public NormObject? Object + { + get + { + NormObject? normObject = null; + var normObjectType = NormObjectGetType(_objectHandle); + switch (normObjectType) + { + case NormObjectType.NORM_OBJECT_DATA: + normObject = new NormData(_objectHandle); + break; + case NormObjectType.NORM_OBJECT_FILE: + normObject = new NormFile(_objectHandle); + break; + case NormObjectType.NORM_OBJECT_STREAM: + normObject= new NormStream(_objectHandle); + break; + case NormObjectType.NORM_OBJECT_NONE: + default: + break; + } + return normObject; + } + } + + /// + /// This function returns a description of the NORM protocol event + /// + /// A string that includes the event type. + public override string ToString() + { + return $"NormEvent [type={_type}]"; + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs new file mode 100644 index 00000000..a627a480 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs @@ -0,0 +1,54 @@ +namespace Mil.Navy.Nrl.Norm +{ + /// + /// A transport object of type NORM_OBJECT_FILE. + /// + public class NormFile : NormObject + { + /// + /// Maximum length of file names. + /// + public const int FILENAME_MAX = 260; + + /// + /// Constructor of NormFile + /// + /// This type is used to reference state kept for data transport objects being actively transmitted or received. + internal NormFile(long handle) : base(handle) + { + } + + /// + /// The name of the file. + /// + /// Thrown when failed to get file name. + public string Name + { + get + { + var buffer = new char[FILENAME_MAX]; + if (!NormFileGetName(_handle, buffer, FILENAME_MAX)) + { + throw new IOException("Failed to get file name"); + } + buffer = buffer.Where(c => c != 0).ToArray(); + return new string(buffer); + } + } + + /// + /// This function renames the file used to store content for the NORM_OBJECT_FILE transport object. + /// This allows receiver applications to rename (or move) received files as needed. + /// NORM uses temporary file names for received files until the application explicitly renames the file. + /// + /// The full path of received file. + /// Thrown when failed to rename file. + public void Rename(string filePath) + { + if(!NormFileRename(_handle, filePath)) + { + throw new IOException("Failed to rename file"); + } + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs new file mode 100644 index 00000000..d8052198 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs @@ -0,0 +1,236 @@ +using Microsoft.Win32.SafeHandles; +using System.Runtime.InteropServices; +using System.Net.Sockets; + +namespace Mil.Navy.Nrl.Norm +{ + /// + /// An instance of a NORM protocol engine + /// + public class NormInstance + { + /// + /// Returned on error when getting the file descriptor for a NormInstance. + /// + public const int NORM_DESCRIPTOR_INVALID = 0; + /// + /// The _handle refers to the NORM protocol engine instance + /// + private long _handle; + + /// + /// Constructor for NormInstance with priority boost + /// + /// The priorityBoost parameter, when set to a value of true, specifies that the NORM protocol engine thread be run with higher priority scheduling. + public NormInstance(bool priorityBoost) + { + CreateInstance(priorityBoost); + } + + /// + /// Default constructor for NormInstance + /// + public NormInstance() : this(false) + { + } + + /// + /// This function creates an instance of a NORM protocol engine and is the necessary first step before any other API functions may be used. + /// + /// The priorityBoost parameter, when set to a value of true, specifies that the NORM protocol engine thread be run with higher priority scheduling. + private void CreateInstance(bool priorityBoost) + { + var handle = NormCreateInstance(priorityBoost); + _handle = handle; + } + + /// + /// The function immediately shuts down and destroys the NORM protocol engine instance referred to by the instanceHandle parameter. + /// + public void DestroyInstance() + { + NormDestroyInstance(_handle); + } + + /// + /// This function creates a NORM protocol session (NormSession) using the address (multicast or unicast) and port + /// parameters provided. While session state is allocated and initialized, active session participation does not begin + /// until the session starts the sender and/or receiver to join the specified multicast group + /// (if applicable) and start protocol operation. + /// + /// Specified address determines the destination of NORM messages sent + /// Valid, unused port number corresponding to the desired NORM session address. + /// Identifies the application's presence in the NormSession + /// The NormSession that was createdThrows when fails to create session + public NormSession CreateSession(string address, int port, long localNodeId) + { + var session = NormCreateSession(_handle, address, port, localNodeId); + if (session == NormSession.NORM_SESSION_INVALID) + { + throw new IOException("Failed to create session"); + } + return new NormSession(session); + } + + /// + /// Determines if the NORM protocol engine instance has a next event + /// + /// The seconds to wait + /// The microseconds to wait + /// True if the NORM protocol engine instance has a next event + public bool HasNextEvent(int sec, int usec) + { + var totalMilliseconds = sec * 1000 + usec / 1000; + var waitTime = TimeSpan.FromMilliseconds(totalMilliseconds); + return HasNextEvent(waitTime); + } + + /// + /// Determines if the NORM protocol engine instance has a next event + /// + /// The time to wait + /// True if the NORM protocol engine instance has a next event + public bool HasNextEvent(TimeSpan waitTime) + { + var normDescriptor = NormGetDescriptor(_handle); + if (normDescriptor == NORM_DESCRIPTOR_INVALID) + { + return false; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + using var eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + eventWaitHandle.SafeWaitHandle = new SafeWaitHandle(new IntPtr(normDescriptor), false); + return eventWaitHandle.WaitOne(waitTime); + } + var hasNextEvent = false; + var timeout = DateTime.Now.Add(waitTime); + while (!hasNextEvent && DateTime.Now <= timeout) + { + using var socketHandle = new SafeSocketHandle(new IntPtr(normDescriptor), false); + using var socket = new Socket(socketHandle); + hasNextEvent = socket.Available > 0; + } + return hasNextEvent; + } + + /// + /// This function retrieves the next available NORM protocol event from the protocol engine. + /// + /// waitForEvent specifies whether the call to this function is blocking or not, if "waitForEvent" is false, this is a non-blocking call. + /// Returns an instance of NormEvent if NormGetNextEvent() returns true, returns null otherwise. + public NormEvent? GetNextEvent(bool waitForEvent) + { + bool success = NormGetNextEvent(_handle, out NormApi.NormEvent normEvent, waitForEvent); + if (!success) + { + return null; + } + return new NormEvent(normEvent.Type, normEvent.Session, normEvent.Sender, normEvent.Object); + } + + /// + /// This function retrieves the next available NORM protocol event from the protocol engine. + /// + /// + /// This is a default function which calls GetNextEvent(bool waitForEvent) override + /// with waitForEvent set as true. + /// + /// Returns an instance of NormEvent if NormGetNextEvent() returns true, returns null otherwise. + public NormEvent? GetNextEvent() + { + return GetNextEvent(true); + } + + /// + /// This function sets the directory path used by receivers to cache newly-received NORM_OBJECT_FILE content. + /// + /// the cachePath is a string specifying a valid (and writable) directory path. + /// Throws when fails to set the cache directory + public void SetCacheDirectory(string cachePath) + { + if(!NormSetCacheDirectory(_handle, cachePath)) + { + throw new IOException("Failed to set the cache directory"); + } + } + + /// + /// This function immediately stops the NORM protocol engine thread. + /// + public void StopInstance() + { + NormStopInstance(_handle); + } + + /// + /// This function creates and starts an operating system thread to resume NORM protocol engine operation that was previously stopped by a call to StopInstance(). + /// + /// Boolean as to the success of the instance restart. + public bool RestartInstance() + { + return NormRestartInstance(_handle); + } + + /// + /// Immediately suspends the NORM protocol engine thread. + /// + /// Boolean as to the success of the instance suspension. + public bool SuspendInstance() + { + return NormSuspendInstance(_handle); + } + + /// + /// This function allows NORM debug output to be directed to a file instead of the default STDERR. + /// + public void ResumeInstance() + { + NormResumeInstance(_handle); + } + + /// + /// This function allows NORM debug output to be directed to a file instead of the default STDERR. + /// + /// Full path and name of the debug log. + /// Throws when fails to open debug log" + public void OpenDebugLog(string fileName) + { + if (!NormOpenDebugLog(_handle, fileName)) + { + throw new IOException("Failed to open debug log"); + } + } + + /// + /// This function disables NORM debug output to be directed to a file instead of the default STDERR. + /// + public void CloseDebugLog() + { + NormCloseDebugLog(_handle); + } + + /// + /// This function allows NORM debug output to be directed to a named pipe. + /// + /// The debug pipe name. + /// Throws when fails to open debug pipe. + public void OpenDebugPipe(string pipename) + { + if (!NormOpenDebugPipe(_handle, pipename)) + { + throw new IOException("Failed to open debug pipe"); + } + } + + /// + /// The currently set debug level. + /// + public int DebugLevel + { + get => NormGetDebugLevel(); + set => NormSetDebugLevel(value); + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs new file mode 100644 index 00000000..dbb9c7d2 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs @@ -0,0 +1,147 @@ +using System.Net; + +namespace Mil.Navy.Nrl.Norm +{ + /// + /// A participant in a NORM protocol session + /// + public class NormNode + { + /// + /// When creating a session, allows the NORM implementation to attempt to pick an identifier based on the host computer's "default" IP address. + /// + public const long NORM_NODE_ANY = 0xffffffff; + /// + /// The special value NORM_NODE_NONE corresponds to an invalid (or null) node. + /// + public const int NORM_NODE_NONE = 0; + /// + /// The special value NORM_NODE_INVALID corresponds to an invalid reference. + /// + public const int NORM_NODE_INVALID = 0; + /// + /// The handle is associated to the NORM protocol engine instance. + /// + private long _handle; + + /// + /// Parameterized contructor. + /// + /// The handle is associated to the NORM protocol engine instance. + internal NormNode(long handle) + { + _handle = handle; + } + + /// + /// The NormNodeId identifier for the remote participant. + /// + public long Id => NormNodeGetId(_handle); + + /// + /// The current network source address detected for packets received from remote NORM sender. + /// + public IPEndPoint Address + { + get + { + var buffer = new byte[256]; + var bufferLength = buffer.Length; + if (!NormNodeGetAddress(_handle, buffer, ref bufferLength, out var port)) + { + throw new IOException("Failed to get node address"); + } + buffer = buffer.Take(bufferLength).ToArray(); + var ipAddressText = string.Join('.', buffer); + var ipAddress = IPAddress.Parse(ipAddressText); + + return new IPEndPoint(ipAddress, port); + } + } + + /// + /// The advertised estimate of group round-trip timing (GRTT) for the remote sender. + /// + public double Grtt => NormNodeGetGrtt(_handle); + + /// + /// NORM application-defined command for transmission. + /// + public byte[] Command + { + get + { + var buffer = new byte[256]; + var bufferLength = buffer.Length; + if (!NormNodeGetCommand(_handle, buffer, ref bufferLength)) + { + throw new IOException("Failed to get command"); + } + return buffer.Take(bufferLength).ToArray(); + } + } + + /// + /// This function controls the destination address of receiver feedback messages generated in response to a specific remote NORM sender. + /// + /// If state is true, "unicast NACKing" is enabled. + public void SetUnicastNack(bool state) + { + NormNodeSetUnicastNack(_handle, state); + } + + /// + /// This function sets the default "nacking mode" used for receiving new objects from a specific sender. + /// + /// Specifies the nacking mode. + public void SetNackingMode(NormNackingMode nackingMode) + { + NormNodeSetNackingMode(_handle, nackingMode); + } + + /// + /// This function allows the receiver application to customize at what points the receiver initiates the NORM NACK repair process during protocol operation. + /// + /// Specifies the repair boundary. + public void SetRepairBoundary(NormRepairBoundary repairBoundary) + { + NormNodeSetRepairBoundary(_handle, repairBoundary); + } + + /// + /// This routine sets the robustFactor as described in NormSetDefaultRxRobustFactor() for an individual remote sender. + /// + /// The robustFactor value determines how + /// many times a NORM receiver will self-initiate NACKing (repair requests) upon cessation of packet reception from + /// a sender. The default value is 20. Setting rxRobustFactor to -1 will make the NORM receiver infinitely persistent + /// (i.e., it will continue to NACK indefinitely as long as it is missing data content). + public void SetRxRobustFactor(int robustFactor) + { + NormNodeSetRxRobustFactor(_handle, robustFactor); + } + + /// + /// This function releases memory resources that were allocated for a remote sender. + /// + public void FreeBuffers() + { + NormNodeFreeBuffers(_handle); + } + + /// + /// This function allows the application to retain state associated even when the underlying NORM protocol engine might normally free the associated state. + /// + public void Retain() + { + NormNodeRetain(_handle); + } + + /// + /// This function releases the Node so that the NORM protocol engine may free associated resources as needed. + /// + public void Release() + { + NormNodeRelease(_handle); + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs new file mode 100644 index 00000000..4d7ffefe --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs @@ -0,0 +1,163 @@ +namespace Mil.Navy.Nrl.Norm +{ + /// + /// The base transport object. + /// + public class NormObject + { + /// + /// The special value NORM_OBJECT_INVALID corresponds to an invalid reference. + /// + public const int NORM_OBJECT_INVALID = 0; + /// + /// Used to reference state kept for data transport objects being actively transmitted or received. + /// + protected long _handle; + + /// + /// Internal constructor for NormObject + /// + /// Identifies an instance of NormObject + internal NormObject(long handle) + { + _handle = handle; + } + + /// + /// Used to reference state kept for data transport objects being actively transmitted or received. + /// + public long Handle => _handle; + + /// + /// Data associated with NormObject. + /// + public byte[]? Info + { + get + { + if (!NormObjectHasInfo(_handle)) + { + return null; + } + var length = NormObjectGetInfoLength(_handle); + var buffer = new byte[length]; + NormObjectGetInfo(_handle, buffer, length); + return buffer; + } + } + + /// + /// The type of Norm object. + /// Valid types include: + /// NORM_OBJECT_FILE + /// NORM_OBJECT_DATA + /// NORM_OBJECT_STREAM + /// + public NormObjectType Type + { + get + { + return NormObjectGetType(_handle); + } + } + + /// + /// The size (in bytes) of the transport object. + /// + public long Size + { + get + { + return NormObjectGetSize(_handle); + } + } + + /// + /// The NormNodeHandle corresponding to the remote sender of the transport object. + /// + /// Thrown when NormObjectGetSender() returns NORM_NODE_INVALID, indicating locally originated sender object. + public long Sender + { + get + { + var sender = NormObjectGetSender(_handle); + if(sender == NormNode.NORM_NODE_INVALID) + { + throw new IOException("Locally originated sender object"); + } + return sender; + } + } + + /// + /// This function sets the "nacking mode" used for receiving a specific transport object. + /// + /// Specifies the nacking mode. + public void SetNackingMode(NormNackingMode nackingMode) + { + NormObjectSetNackingMode(_handle, nackingMode); + } + + /// + /// This function can be used to determine the progress of reception of the NORM transport object + /// + /// A number of object source data bytes pending reception (or transmission) is returned. + public long GetBytesPending() + { + return NormObjectGetBytesPending(_handle); + } + + /// + /// This function immediately cancels the transmission of a local sender transport object or the reception of a specified + /// object from a remote sender. + /// + public void Cancel() + { + NormObjectCancel(_handle); + } + + /// + /// This function "retains" the objectHandle and any state associated with it for further use by the application even + /// when the NORM protocol engine may no longer require access to the associated transport object. + /// + public void Retain() + { + NormObjectRetain(_handle); + } + + /// + /// This function complements the Retain() call by immediately freeing any resources associated with + /// the given objectHandle, assuming the underlying NORM protocol engine no longer requires access to the corresponding + /// transport object. Note the NORM protocol engine retains/releases state for associated objects for its own + /// needs and thus it is very unsafe for an application to call Release() for an objectHandle for which + /// it has not previously explicitly retained via Retain(). + /// + public void Release() + { + NormObjectRelease(_handle); + } + + /// + /// Gets the hash code of the object. + /// + /// Returns the handle of the object. + public override int GetHashCode() + { + return (int)_handle; + } + + /// + /// Decides wether specified object is equal another. + /// + /// NormObject to compare to. + /// Returns true if two objects equal, false otherwise. + public override bool Equals(object? obj) + { + if(obj is NormObject) + { + return _handle == ((NormObject)obj).Handle; + } + return false; + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs new file mode 100644 index 00000000..de68d54c --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs @@ -0,0 +1,923 @@ +using System.Runtime.CompilerServices; +using System.Text; + +namespace Mil.Navy.Nrl.Norm +{ + /// + /// NORM transport session + /// + public class NormSession + { + /// + /// The special value NORM_SESSION_INVALID is used to refer to invalid session references. + /// + public const int NORM_SESSION_INVALID = 0; + /// + /// A dictionary of NORM sessions with their respective handles. + /// + private static Dictionary _normSessions = new Dictionary(); + /// + /// The NormSessionHandle type is used to reference the NORM transport session. + /// + private long _handle; + + /// + /// Used for the application's participation in the NormSession. + /// + public long LocalNodeId + { + get => NormGetLocalNodeId(_handle); + } + + /// + /// The report interval. + /// + public double ReportInterval + { + get => NormGetReportInterval(_handle); + set => NormSetReportInterval(_handle, value); + } + + /// + /// Transmission rate (in bits per second (bps)) limit used for NormSender transmissions. + /// + public double TxRate + { + get => NormGetTxRate(_handle); + set => NormSetTxRate(_handle, value); + } + + /// + /// Group round-trip timing. + /// + public double GrttEstimate + { + get => NormGetGrttEstimate(_handle); + set => NormSetGrttEstimate(_handle, value); + } + + /// + /// Internal constructor of NormSession. + /// + /// Used to identify application in the NormSession. + /// The handle and NormSession are added to the dictionary of NORM sessions. + internal NormSession(long handle) + { + _handle = handle; + lock (_normSessions) + { + _normSessions.Add(handle, this); + } + } + + /// + /// Get a specified NormSession from the dictionary of NORM sessions. + /// + /// Specifies the session to return. + /// Returns a NormSession. + [MethodImpl(MethodImplOptions.Synchronized)] + internal static NormSession GetSession(long handle) + { + return _normSessions[handle]; + } + + /// + /// This function immediately terminates the application's participation in the NormSession and frees any resources used by that session. + /// + public void DestroySession() + { + lock (_normSessions) + { + _normSessions.Remove(_handle); + } + DestroySessionNative(); + } + + /// + /// This function immediately terminates the application's participation in the NormSession and frees any resources used by that session. + /// + private void DestroySessionNative() + { + NormDestroySession(_handle); + } + + /// + /// This function is used to force NORM to use a specific port number for UDP packets sent. + /// + /// + /// This is a default function which calls SetTxPort(int port, bool enableReuse, string? txBindAddress) override + /// with enableReuse set as false and txBindAddress set to null. + /// + /// The port parameter, specifies which port number to use. + /// Thrown when NormSetTxPort() returns false, indicating the failure to set tx port. + public void SetTxPort(int port) + { + SetTxPort(port, false, null); + } + + /// + /// This function is used to force NORM to use a specific port number for UDP packets sent. + /// + /// The port parameter, specifies which port number to use. + /// When set to true, allows that the specified port may be reused for multiple sessions. + /// The txBindAddress parameter allows specification of a specific source address binding for packet transmission. + /// Thrown when NormSetTxPort() returns false, indicating the failure to set tx port. + public void SetTxPort(int port, bool enableReuse, string? txBindAddress) + { + if (!NormSetTxPort(_handle, port, enableReuse, txBindAddress)) + { + throw new IOException("Failed to set Tx Port"); + } + } + + /// + /// This function limits the NormSession to perform NORM sender functions only. + /// + /// + /// This is a default function which calls etTxOnly(bool txOnly, bool connectToSessionAddress) override + /// with connectToSessionAddress set as false. + /// + /// Boolean specifing whether to turn on or off the txOnly operation. + public void SetTxOnly(bool txOnly) + { + SetTxOnly(txOnly, false); + } + + /// + /// This function limits the NormSession to perform NORM sender functions only. + /// + /// Boolean specifing whether to turn on or off the txOnly operation. + /// The optional connectToSessionAddress parameter, when set to true, causes the underlying NORM code to + /// "connect()" the UDP socket to the session (remote receiver) address and port number. + public void SetTxOnly(bool txOnly, bool connectToSessionAddress) + { + NormSetTxOnly(_handle, txOnly, connectToSessionAddress); + } + + /// + /// This function allows the user to control the port reuse and binding behavior for the receive socket. + /// + /// + /// This is a default function which calls SetRxPortReuse(bool enable, string? rxBindAddress, string? senderAddress, int senderPort) + /// with rxBindAddress set to null, senderAddress set to null, and senderPort set to 0. + /// + /// When the enable parameter is set to true, reuse of the NormSession port number by multiple NORM instances or sessions is enabled. + public void SetRxPortReuse(bool enable) + { + SetRxPortReuse(enable, null, null, 0); + } + + /// + /// This function allows the user to control the port reuse and binding behavior for the receive socket. + /// + /// When the enable parameter is set to true, reuse of the NormSession port number by multiple NORM instances or sessions is enabled. + /// If the optional rxBindAddress is supplied (an IP address or host name in string form), + /// the socket will bind() to the given address when it is opened in a call to StartReceiver() or StartSender(). + /// The optional senderAddress parameter can be used to connect() the underlying NORM receive socket to specific address. + /// The optional senderPort parameter can be used to connect() the underlying NORM receive socket to specific port. + public void SetRxPortReuse(bool enable, string? rxBindAddress, string? senderAddress, int senderPort) + { + NormSetRxPortReuse(_handle, enable, rxBindAddress, senderAddress, senderPort); + } + + /// + /// This is a default function which calls SetEcnSupport(bool ecnEnable, bool ignoreLoss, bool tolerateLoss) override + /// with tolerateLoss set as false. + /// + /// Enables NORM ECN (congestion control) support. + /// With "ecnEnable", use ECN-only, ignoring packet loss. + public void SetEcnSupport(bool ecnEnable, bool ignoreLoss) + { + SetEcnSupport(ecnEnable, ignoreLoss, false); + } + + /// Enables NORM ECN (congestion control) support. + /// With "ecnEnable", use ECN-only, ignoring packet loss. + /// Loss-tolerant congestion control, ecnEnable or not. + public void SetEcnSupport(bool ecnEnable, bool ignoreLoss, bool tolerateLoss) + { + NormSetEcnSupport(_handle, ecnEnable, ignoreLoss, tolerateLoss); + } + + /// + /// This function specifies which host network interface is used for IP Multicast transmissions and group membership. + /// This should be called before any call to StartSender() or StartReceiver() is made so that the IP multicast + /// group is joined on the proper host interface. + /// + /// Name of the interface + /// Thrown when NormSetMulticastInterface() returns false, indicating the failure to set multicast interface. + public void SetMulticastInterface(string interfaceName) + { + if(!NormSetMulticastInterface(_handle, interfaceName)) + { + throw new IOException("Failed to set multicast interface"); + } + } + + /// + /// This function sets the source address for Source-Specific Multicast (SSM) operation. + /// + /// Address to be set as source for source-specific multicast operation. + /// Thrown when NormSetSSM() returns false, indicating the failure to set ssm. + public void SetSSM(string sourceAddress) + { + if(!NormSetSSM(_handle, sourceAddress)) + { + throw new IOException("Failed to set SSM"); + } + } + + /// + /// This function specifies the time-to-live (ttl) for IP Multicast datagrams generated by NORM for the specified + /// sessionHandle. The IP TTL field limits the number of router "hops" that a generated multicast packet may traverse + /// before being dropped. + /// + /// If TTL is equal to one, the transmissions will be limited to the local area network + /// (LAN) of the host computers network interface. Larger TTL values should be specified to span large networks. + /// Thrown when NormSetTTL() returns false, indicating the failure to set ttl. + public void SetTTL(byte ttl) + { + if (!NormSetTTL(_handle, ttl)) + { + throw new IOException("Failed to set TTL"); + } + } + + /// + /// This function specifies the type-of-service (tos) field value used in IP Multicast datagrams generated by NORM. + /// + /// The IP TOS field value can be used as an indicator that a "flow" of packets may merit special Quality-of-Service (QoS) treatment by network devices. + /// Users should refer to applicable QoS information for their network to determine the expected interpretation and treatment (if any) of packets with explicit TOS marking. + /// Thrown when NormSetTOS() returns false, indicating the failure to set TOS. + public void SetTOS(byte tos) + { + if(!NormSetTOS(_handle, tos)) + { + throw new IOException("Failed to set TOS"); + } + } + + /// + /// This function enables or disables loopback operation. + /// + /// If loopbackEnable is set to true, loopback operation is enabled which allows the application to receive its own message traffic. + /// Thus, an application which is both actively receiving and sending may receive its own transmissions. + /// Thrown when NormSetLoopback() returns false, indicating the failure to set loopback. + public void SetLoopback(bool loopbackEnable) + { + if (!NormSetLoopback(_handle, loopbackEnable)) + { + throw new IOException("Failed to set loopback"); + } + } + + public void SetMessageTrace(bool flag) + { + NormSetMessageTrace(_handle, flag); + } + + public void SetTxLoss(double precent) + { + NormSetTxLoss(_handle, precent); + } + + public void SetRxLoss(double precent) + { + NormSetRxLoss(_handle, precent); + } + + /// + /// This function controls a scaling factor that is used for sender timer-based flow control. + /// Timer-based flow control works by preventing the NORM sender application from enqueueing + /// new transmit objects or stream data that would purge "old" objects or stream data when there has been recent + /// NACK activity for those old objects or data. + /// + /// The precent is used to compute a delay time for when a sender buffered object (or block of stream + /// data) may be released (i.e. purged) after transmission or applicable NACKs reception. + public void SetFlowControl(double precent) + { + NormSetFlowControl(_handle, precent); + } + + /// + /// This function can be used to set a non-default socket buffer size for the UDP socket used for data transmission. + /// + /// The bufferSize parameter specifies the desired socket buffer size in bytes. + /// Thrown when NormSetTxSocketBuffer() returns false, indicating the failure to set tx socket buffer. + /// Possible failure modes include an invalid sessionHandle parameter, a call to StartReceiver() or StartSender() has not yet been made for the + /// session, or an invalid bufferSize was given. + /// Note some operating systems may require additional system configuration to use non-standard socket buffer sizes. + public void SetTxSocketBuffer(long bufferSize) + { + if(!NormSetTxSocketBuffer(_handle, bufferSize)) + { + throw new IOException("Failed to set tx socket buffer"); + } + } + + /// + /// This function enables (or disables) the NORM sender congestion control operation. + /// For best operation, this function should be called before the call to StartSender() is made, + /// but congestion control operation can be dynamically enabled/disabled during the course of sender operation. + /// + /// + /// This is the default function which calls SetCongestionControl(bool enable, bool adjustRate) override + /// with adjustRate set to true. + /// + /// Specifies whether to enable or disable the NORM sender congestion control operation. + public void SetCongestionControl(bool enable) + { + SetCongestionControl(enable, true); + } + + /// + /// This function enables (or disables) the NORM sender congestion control operation. + /// For best operation, this function should be called before the call to StartSender() is made, + /// but congestion control operation can be dynamically enabled/disabled during the course of sender operation. + /// + /// Specifies whether to enable or disable the NORM sender congestion control operation. + /// The rate set by SetTxRate() has no effect when congestion control operation is enabled, unless the adjustRate + /// parameter here is set to false. When the adjustRate parameter is set to false, the NORM Congestion Control + /// operates as usual, with feedback collected from the receiver set and the "current limiting receiver" identified, except + /// that no actual adjustment is made to the sender's transmission rate. + public void SetCongestionControl(bool enable, bool adjustRate) + { + NormSetCongestionControl(_handle, enable, adjustRate); + } + + /// + /// This function sets the range of sender transmission rates within which the NORM congestion control algorithm is allowed to operate. + /// + /// rateMin corresponds to the minimum transmission rate (bps). + /// rateMax corresponds to the maximum transmission rate (bps). + public void SetTxRateBounds(double rateMin, double rateMax) + { + NormSetTxRateBounds(_handle, rateMin, rateMax); + } + + /// + /// This function sets limits that define the number and total size of pending transmit objects a NORM sender will allow to be enqueued by the application. + /// + /// The sizeMax parameter sets the maximum total size, in bytes, of enqueued objects allowed. + /// The countMin parameter sets the minimum number of objects the application may enqueue, regardless of the objects' sizes and the sizeMax value. + /// The countMax parameter sets a ceiling on how many objects may be enqueued, regardless of their total sizes with respect to the sizeMax setting. + public void SetTxCacheBounds(long sizeMax, long countMin, long countMax) + { + NormSetTxCacheBounds(_handle,sizeMax, countMin, countMax); + } + + /// + /// The application's participation as a sender begins when this function is called. + /// + /// Application-defined value used as the instance_id field of NORM sender messages for the application's participation within a session. + /// This specifies the maximum memory space (in bytes) the NORM protocol engine is allowed to use to buffer any sender calculated FEC segments and repair state for the session. + /// This parameter sets the maximum payload size (in bytes) of NORM sender messages (not including any NORM message header fields). + /// This parameter sets the number of source symbol segments (packets) per coding block, for the systematic Reed-Solomon FEC code used in the current NORM implementation. + /// This parameter sets the maximum number of parity symbol segments (packets) the sender is willing to calculate per FEC coding block. + /// Sets the NormFecType. + /// Thrown when NormStartSender() returns false, indicating the failure to start sender. + public void StartSender(int sessionId, long bufferSpace, int segmentSize, short blockSize, short numParity, NormFecType fecId) + { + if (!NormStartSender(_handle, sessionId, bufferSpace, segmentSize, blockSize, numParity, fecId)) + { + throw new IOException("Failed to start sender"); + } + } + + /// + /// The application's participation as a sender begins when this function is called. + /// + /// Application-defined value used as the instance_id field of NORM sender messages for the application's participation within a session. + /// This specifies the maximum memory space (in bytes) the NORM protocol engine is allowed to use to buffer any sender calculated FEC segments and repair state for the session. + /// This parameter sets the maximum payload size (in bytes) of NORM sender messages (not including any NORM message header fields). + /// This parameter sets the number of source symbol segments (packets) per coding block, for the systematic Reed-Solomon FEC code used in the current NORM implementation. + /// This parameter sets the maximum number of parity symbol segments (packets) the sender is willing to calculate per FEC coding block. + /// Uses NormFecType.RS for fecId. + /// Thrown when NormStartSender() returns false, indicating the failure to start sender. + public void StartSender(int sessionId, long bufferSpace, int segmentSize, short blockSize, short numParity) + { + StartSender(sessionId, bufferSpace, segmentSize, blockSize, numParity, NormFecType.RS); + } + + /// + /// The application's participation as a sender begins when this function is called. + /// + /// This specifies the maximum memory space (in bytes) the NORM protocol engine is allowed to use to buffer any sender calculated FEC segments and repair state for the session. + /// This parameter sets the maximum payload size (in bytes) of NORM sender messages (not including any NORM message header fields). + /// This parameter sets the number of source symbol segments (packets) per coding block, for the systematic Reed-Solomon FEC code used in the current NORM implementation. + /// This parameter sets the maximum number of parity symbol segments (packets) the sender is willing to calculate per FEC coding block. + /// Sets the NormFecType. + /// Generates a random sessionId. + /// Thrown when NormStartSender() returns false, indicating the failure to start sender. + public void StartSender(long bufferSpace, int segmentSize, short blockSize, short numParity, NormFecType fecId) + { + var sessionId = NormGetRandomSessionId(); + StartSender(sessionId, bufferSpace, segmentSize, blockSize, numParity, fecId); + } + + /// + /// The application's participation as a sender begins when this function is called. + /// + /// This specifies the maximum memory space (in bytes) the NORM protocol engine is allowed to use to buffer any sender calculated FEC segments and repair state for the session. + /// This parameter sets the maximum payload size (in bytes) of NORM sender messages (not including any NORM message header fields). + /// This parameter sets the number of source symbol segments (packets) per coding block, for the systematic Reed-Solomon FEC code used in the current NORM implementation. + /// This parameter sets the maximum number of parity symbol segments (packets) the sender is willing to calculate per FEC coding block. + /// Generates a random sessionId and uses NormFecType.RS for fecId. + /// Thrown when NormStartSender() returns false, indicating the failure to start sender. + public void StartSender(long bufferSpace, int segmentSize, short blockSize, short numParity) + { + StartSender(bufferSpace, segmentSize, blockSize, numParity, NormFecType.RS); + } + + /// + /// This function terminates the application's participation in a NormSession as a sender. By default, the sender will + /// immediately exit the session without notifying the receiver set of its intention. + /// + public void StopSender() + { + NormStopSender(_handle); + } + + /// + /// This function enqueues a file for transmission. + /// + /// + /// This is an overload which will call FileEnqueue() with info set to encoding of the filename, infoOffset set to 0, and infoLength set to info.Length. + /// + /// The fileName parameter specifies the path to the file to be transmitted. The NORM protocol engine + /// read and writes directly from/to file system storage for file transport, potentially providing for a very large virtual + /// "repair window" as needed for some applications. While relative paths with respect to the "current working directory" + /// may be used, it is recommended that full paths be used when possible. + /// A NormFile is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormFileEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue file. + public NormFile FileEnqueue(string filename) + { + var info = Encoding.ASCII.GetBytes(filename); + return FileEnqueue(filename, info, 0, info.Length); + } + + /// + /// This function enqueues a file for transmission. + /// + /// The fileName parameter specifies the path to the file to be transmitted. The NORM protocol engine + /// read and writes directly from/to file system storage for file transport, potentially providing for a very large virtual + /// "repair window" as needed for some applications. While relative paths with respect to the "current working directory" + /// may be used, it is recommended that full paths be used when possible. + /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. + /// Indicates the strart of the message. Anything before it will not be sent. + /// Note: to send full message infoOffset should be set to 0. + /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. + /// A NormFile is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormFileEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue file. + public NormFile FileEnqueue(string filename, byte[] info, int infoOffset, int infoLength) + { + byte[]? infoBytes; + if (info != null) + { + infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); + } + else + { + infoBytes = null; + infoLength = 0; + } + var objectHandle = NormFileEnqueue(_handle, filename, infoBytes, infoLength); + if (objectHandle == NormObject.NORM_OBJECT_INVALID) + { + throw new IOException("Failed to enqueue file"); + } + return new NormFile(objectHandle); + } + + /// + /// This function enqueues a segment of application memory space for transmission. + /// + /// + /// This is an overload which will call DataEnqueue() with info set to null, infoOffset set to 0, and infoLength set to 0. + /// + /// The dataBuffer is a byte array containing the message to be transmitted. + /// Indicates the strart of the message. Anything before it will not be sent. + /// Note: to send full message dataOffset should be set to 0. + /// Size of the message. + /// A NormData is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. + public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) + { + return DataEnqueue(dataBuffer, dataOffset, dataLength, null, 0, 0); + } + + /// + /// This function enqueues a segment of application memory space for transmission. + /// + /// The dataBuffer is a byte array containing the message to be transmitted. + /// Indicates the strart of the message. Anything before it will not be sent. + /// Note: to send full message dataOffset should be set to 0. + /// Size of the message. + /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. + /// Indicates the strart of the message. + /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. + /// A NormData is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. + public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength) + { + var dataBytes = dataBuffer.Skip(dataOffset).Take(dataLength).ToArray(); + byte[]? infoBytes; + if (info != null) + { + infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); + } + else + { + infoBytes = null; + infoLength = 0; + } + var objectHandle = NormDataEnqueue(_handle, dataBytes, dataLength, infoBytes, infoLength); + if (objectHandle == NormObject.NORM_OBJECT_INVALID) + { + throw new IOException("Failed to enqueue data"); + } + return new NormData(objectHandle); + } + + /// + /// This function opens a NORM_OBJECT_STREAM sender object and enqueues it for transmission. + /// + /// + /// No data is sent until subsequent calls to StreamWrite() are made unless + /// NORM_INFO content is specified for the stream with the info and infoLength parameters. Example usage of + /// NORM_INFO content for NORM_OBJECT_STREAM might include application-defined data typing or other information + /// which will enable NORM receiver applications to properly interpret the received stream as it is being received. + /// This is an overload which will call StreamOpen() with info set to null, infoOffset set to 0, and infoLength set to 0. + /// + /// + /// The bufferSize parameter controls the size of the stream's "repair window" + /// which limits how far back the sender will "rewind" to satisfy receiver repair requests. + /// + /// A NormStream is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormStreamOpen() returns NORM_OBJECT_INVALID, indicating the failure to open stream. + public NormStream StreamOpen(long bufferSize) + { + return StreamOpen(bufferSize, null, 0, 0); + } + + /// + /// This function opens a NORM_OBJECT_STREAM sender object and enqueues it for transmission. + /// + /// + /// No data is sent until subsequent calls to NormStreamWrite() are made unless + /// NORM_INFO content is specified for the stream with the info and infoLength parameters. Example usage of + /// NORM_INFO content for NORM_OBJECT_STREAM might include application-defined data typing or other information + /// which will enable NORM receiver applications to properly interpret the received stream as it is being received. + /// + /// The bufferSize parameter controls the size of the stream's "repair window" + /// which limits how far back the sender will "rewind" to satisfy receiver repair requests. + /// Alloted memory space for transmitted information. + /// Indicates the strart of the message. Anything before it will not be sent. + /// Note: to send full message infoOffset should be set to 0. + /// Size of the message. + /// A NormStream is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormStreamOpen() returns NORM_OBJECT_INVALID, indicating the failure to open stream. + public NormStream StreamOpen(long bufferSize, byte[]? info, int infoOffset, int infoLength) + { + byte[]? infoBytes; + if (info != null) + { + infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); + } + else + { + infoBytes = null; + infoLength = 0; + } + var objectHandle = NormStreamOpen(_handle, bufferSize, infoBytes, infoLength); + if (objectHandle == NormObject.NORM_OBJECT_INVALID) + { + throw new IOException("Failed to open stream"); + } + return new NormStream(objectHandle); + } + + /// + /// This function initiates the application's participation as a receiver within the NormSession. + /// + /// The bufferSpace parameter is used to set a limit on the amount of bufferSpace allocated + /// by the receiver per active NormSender within the session. + /// Thrown when NormStartReceiver() returns false, indicating the failure to start receiver. + public void StartReceiver(long bufferSpace) + { + if(!NormStartReceiver(_handle, bufferSpace)) + { + throw new IOException("Failed to start receiver"); + } + } + + /// + /// This function ends the application's participation as a receiver in the NormSession. + /// + public void StopReceiver() + { + NormStopReceiver(_handle); + } + + /// + /// This function sets the quantity of proactive "auto parity" NORM_DATA messages sent at the end of each FEC coding + /// block. By default (i.e., autoParity = 0), FEC content is sent only in response to repair requests (NACKs) from receivers. + /// + /// Setting a non-zero value for autoParity, the sender can automatically accompany each coding + /// block of transport object source data segments ((NORM_DATA messages) with the set number of FEC segments. + public void SetAutoParity(short autoParity) + { + NormSetAutoParity(_handle, autoParity); + } + + /// + /// This function sets the sender's maximum advertised GRTT value. + /// + /// The grttMax parameter, in units of seconds, limits the GRTT used by the group for scaling protocol timers, regardless + /// of larger measured round trip times. The default maximum for the NRL NORM library is 10 seconds. + public void SetGrttMax(double grttMax) + { + NormSetGrttMax(_handle, grttMax); + } + + /// + /// This function sets the sender's mode of probing for round trip timing measurement responses from the receiver. + /// + /// Possible values for the probingMode parameter include NORM_PROBE_NONE, NORM_PROBE_PASSIVE, and NORM_PROBE_ACTIVE. + public void SetGrttProbingMode(NormProbingMode probingMode) + { + NormSetGrttProbingMode(_handle, probingMode); + } + + /// + /// This function controls the sender GRTT measurement and estimation process. + /// The NORM sender multiplexes periodic transmission of NORM_CMD(CC) messages with its ongoing data transmission + /// or when data transmission is idle.When NORM congestion control operation is enabled, these probes are sent + /// once per RTT of the current limiting receiver(with respect to congestion control rate). In this case the intervalMin + /// and intervalMax parameters (in units of seconds) control the rate at which the sender's estimate of GRTT is updated. + /// + /// At session start, the estimate is updated at intervalMin and the update interval time is doubled until intervalMax is reached. + /// At session start, the estimate is updated at intervalMin and the update interval time is doubled until intervalMax is reached. + public void SetGrttProbingInterval(double intervalMin, double intervalMax) + { + NormSetGrttProbingInterval(_handle, intervalMin, intervalMax); + } + + /// + /// This function sets the sender's "backoff factor". + /// + /// The backoffFactor (in units of seconds) is used to scale various timeouts related to the NACK repair process. + public void SetBackoffFactor(double backoffFactor) + { + NormSetBackoffFactor(_handle, backoffFactor); + } + + /// + /// This function sets the sender's estimate of receiver group size. + /// + /// The sender advertises its groupSize setting to the receiver group in NORM protocol message + /// headers that, in turn, use this information to shape the distribution curve of their random timeouts for the timer-based, + /// probabilistic feedback suppression technique used in the NORM protocol. + public void SetGroupSize(long groupSize) + { + NormSetGroupSize(_handle, groupSize); + } + + /// + /// This routine sets the "robustness factor" used for various NORM sender functions. These functions include the + /// number of repetitions of "robustly-transmitted" NORM sender commands such as NORM_CMD(FLUSH) or similar + /// application-defined commands, and the number of attempts that are made to collect positive acknowledgement + /// from receivers.These commands are distinct from the NORM reliable data transmission process, but play a role + /// in overall NORM protocol operation. + /// + /// The default txRobustFactor value is 20. This relatively large value makes + /// the NORM sender end-of-transmission flushing and positive acknowledgement collection functions somewhat immune from packet loss. + /// Setting txRobustFactor to a value of -1 makes the redundant transmission of these commands continue indefinitely until completion. + public void SetTxRobustFactor(int txRobustFactor) + { + NormSetTxRobustFactor(_handle, txRobustFactor); + } + + /// + /// This function allows the application to resend (or reset transmission of) a NORM_OBJECT_FILE or NORM_OBJECT_DATA + /// transmit object that was previously enqueued for the session. + /// + /// The normObject parameter must be a valid transmit NormObject that has not yet been "purged" from the sender's transmit queue. + /// Thrown when NormRequeueObject() returns false, indicating the failure to requeue object. + public void RequeueObject(NormObject normObject) + { + if(!NormRequeueObject(_handle, normObject.Handle)) + { + throw new IOException("Failed to requeue object"); + } + } + + /// + /// This function specifies a "watermark" transmission point at which NORM sender protocol operation should perform + /// a flushing process and/or positive acknowledgment collection for a given sessionHandle. + /// + /// This is an overload which will call the SetWatermark() override with overrideFlush set as false. + /// The normObject parameter must be a valid transmit NormObject that has not yet been "purged" from the sender's transmit queue. + /// Thrown when NormSetWatermark() returns false, indicating the failure to set watermark. + public void SetWatermark(NormObject normObject) + { + SetWatermark(normObject, false); + } + + /// + /// This function specifies a "watermark" transmission point at which NORM sender protocol operation should perform + /// a flushing process and/or positive acknowledgment collection for a given sessionHandle. + /// + /// The normObject parameter must be a valid transmit NormObject that has not yet been "purged" from the sender's transmit queue. + /// The optional overrideFlush parameter, when set to true, causes the watermark acknowledgment process that is + /// established with this function call to potentially fully supersede the usual NORM end-of-transmission flushing + /// process that occurs. If overrideFlush is set and the "watermark" transmission point corresponds to the last + /// transmission that will result from data enqueued by the sending application, then the watermark flush completion + /// will terminate the usual flushing process + /// Thrown when NormSetWatermark() returns false, indicating the failure to set watermark. + public void SetWatermark(NormObject normObject, bool overrideFlush) + { + if(!NormSetWatermark(_handle, normObject.Handle, overrideFlush)) + { + throw new IOException("Failed to set watermark"); + } + } + + /// + /// This function cancels any "watermark" acknowledgement request that was previously set via the SetWatermark() function. + /// + public void CancelWatermark() + { + NormCancelWatermark(_handle); + } + + /// Thrown when NormResetWatermark() returns false, indicating the failure to reset watermark. + public void ResetWatermark() + { + if(!NormResetWatermark(_handle)) + { + throw new IOException("Failed to reset watermark"); + } + } + + /// + /// When this function is called, the specified nodeId is added to the list of NormNodeId values (i.e., the "acking node" + /// list) used when NORM sender operation performs positive acknowledgement (ACK) collection. + /// + /// Identifies the application's presence in the NormSession. + /// Thrown when NormAddAckingNode() returns false, indicating the failure to add acking node. + public void AddAckingNode(long nodeId) + { + if(!NormAddAckingNode(_handle, nodeId)) + { + throw new IOException("Failed to add acking node"); + } + } + + /// + /// This function deletes the specified nodeId from the list of NormNodeId values used when NORM sender operation + /// performs positive acknowledgement (ACK) collection. + /// + /// Identifies the application's presence in the NormSession. + public void RemoveAckingNode(long nodeId) + { + NormRemoveAckingNode(_handle, nodeId); + } + + /// + /// This function queries the status of the watermark flushing process and/or positive acknowledgment collection + /// initiated by a prior call to SetWatermark(). + /// + /// Identifies the application's presence in the NormSession. + /// + /// Possible return values include: + /// NORM_ACK_INVALID - The given sessionHandle is invalid or the given nodeId is not in the sender's acking list. + /// NORM_ACK_FAILURE - The positive acknowledgement collection process did not receive acknowledgment from every listed receiver (nodeId = NORM_NODE_ANY) or the identified nodeId did not respond. + /// NORM_ACK_PENDING - The flushing process at large has not yet completed (nodeId = NORM_NODE_ANY) or the given individual nodeId is still being queried for response. + /// NORM_ACK_SUCCESS - All receivers (nodeId = NORM_NODE_ANY) responded with positive acknowledgement or the given specific nodeId did acknowledge. + /// + public NormAckingStatus GetAckingStatus(long nodeId) + { + return NormGetAckingStatus(_handle, nodeId); + } + + /// + /// This function enqueues a NORM application-defined command for transmission. + /// + /// The cmdBuffer parameter points to a buffer containing the application-defined command content that will be contained in the NORM_CMD(APPLICA-TION) message payload. + /// The cmdLength indicates the length of this content (in bytes) and MUST be less than or equal to the segmentLength value for the given session. + /// The command is NOT delivered reliably, + /// but can be optionally transmitted with repetition (once per GRTT) according to the NORM transmit robust factor + /// value for the given session if the robust parameter is set to true. + /// Thrown when NormSendCommand() returns false, indicating the failure to send command. + public void SendCommand(byte[] cmdBuffer, int cmdLength, bool robust) + { + if(!NormSendCommand(_handle, cmdBuffer, cmdLength, robust)) + { + throw new IOException("Failed to send command"); + } + } + + /// + /// This function terminates any pending NORM_CMD(APPLICATION) transmission that was previously initiated with the SendCommand() call. + /// + public void CancelCommand() + { + NormCancelCommand(_handle); + } + + /// + /// This function sets a limit on the number of outstanding (pending) NormObjects for which a receiver will keep state on a per-sender basis. + /// + /// The value countMax sets a limit on the maximum consecutive range of objects that can be pending. + public void SetRxCacheLimit(int countMax) + { + NormSetRxCacheLimit(_handle, countMax); + } + + /// + /// This function allows the application to set an alternative, non-default buffer size for the UDP socket for packet reception. + /// + /// The bufferSize parameter specifies the socket buffer size in bytes. + /// Thrown when NormSetRxSocketBuffer() returns false, indicating the failure to set rx socket buffer. + public void SetRxSocketBuffer(long bufferSize) + { + if(!NormSetRxSocketBuffer(_handle, bufferSize)) + { + throw new IOException("Failed to set rx socket buffer"); + } + } + + /// + /// This function provides the option to configure a NORM receiver application as a "silent receiver". This mode of + /// receiver operation dictates that the host does not generate any protocol messages while operating as a receiver. + /// + /// Setting the silent parameter to true enables silent receiver operation while + /// setting it to false results in normal protocol operation where feedback is provided as needed for reliability and + /// protocol operation. + /// When the maxDelay parameter is set to a non-negative value, the value determines the maximum number + /// of FEC coding blocks (according to a NORM sender's current transmit position) the receiver will cache an incompletely-received + /// FEC block before giving the application the (incomplete) set of received source segments. + public void SetSilentReceiver(bool silent, int maxDelay) + { + NormSetSilentReceiver(_handle, silent, maxDelay); + } + + /// + /// This function controls the default behavior determining the destination of receiver feedback messages generated + /// while participating in the session. + /// + /// If the enable parameter is true, "unicast NACKing" is enabled for new remote + /// senders while it is disabled for state equal to false. + public void SetDefaultUnicastNack(bool enable) + { + NormSetDefaultUnicastNack(_handle, enable); + } + + /// + /// This function sets the default "synchronization policy" used when beginning (or restarting) reception of objects + /// from a remote sender (i.e., "syncing" to the sender). + /// + /// The "synchronization policy" is the behavior observed by the receiver with regards to what objects it attempts to reliably receive + /// (via transmissions of Negative Acknowledgements to the sender(s) or group as needed). + public void SetDefaultSyncPolicy(NormSyncPolicy syncPolicy) + { + NormSetDefaultSyncPolicy(_handle, syncPolicy); + } + + /// + /// This function sets the default "nacking mode" used when receiving objects. + /// This allows the receiver application some control of its degree of participation in the repair process. By limiting receivers + /// to only request repair of objects in which they are really interested in receiving, some overall savings in unnecessary + /// network loading might be realized for some applications and users. + /// + /// Specifies the nacking mode. + public void SetDefaultNackingMode(NormNackingMode nackingMode) + { + NormSetDefaultNackingMode(_handle, nackingMode); + } + + /// + /// This function allows the receiver application to customize at what points the receiver + /// initiates the NORM NACK repair process during protocol operation. + /// + /// Specifies the repair boundary. + public void SetDefaultRepairBoundary(NormRepairBoundary repairBoundary) + { + NormSetDefaultRepairBoundary(_handle, repairBoundary); + } + + /// + /// This routine controls how persistently NORM receivers will maintain state for sender(s) and continue to request + /// repairs from the sender(s) even when packet reception has ceased. + /// + /// The rxRobustFactor value determines how + /// many times a NORM receiver will self-initiate NACKing (repair requests) upon cessation of packet reception from + /// a sender. The default value is 20. Setting rxRobustFactor to -1 will make the NORM receiver infinitely persistent + /// (i.e., it will continue to NACK indefinitely as long as it is missing data content). + public void SetDefaultRxRobustFactor(int rxRobustFactor) + { + NormSetDefaultRxRobustFactor(_handle, rxRobustFactor); + } + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs new file mode 100644 index 00000000..4b443dd3 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs @@ -0,0 +1,184 @@ +namespace Mil.Navy.Nrl.Norm +{ + /// + /// A transport object of type NORM_OBJECT_STREAM. + /// + public class NormStream : NormObject + { + /// + /// This boolean tells whether the transmit stream, specified by the streamHandle parameter, + /// has buffer space available so that the application may successfully make a call to NormStreamWrite(). + /// + public bool HasVacancy + { + get + { + return NormStreamHasVacancy(_handle); + } + } + + /// + /// The current read offset value for the receive stream. + /// + public long ReadOffset + { + get + { + return NormStreamGetReadOffset(_handle); + } + } + + /// + /// Internal constructor for NormStream + /// + /// The handle is associated to the NORM protocol engine instance + internal NormStream(long handle) : base(handle) + { + } + + /// + /// This function enqueues data for transmission within the NORM stream. + /// + /// The buffer parameter must be a pointer to the data to be enqueued. + /// The offset indicated where in the buffer to start writing the data. + /// Note: If the data is written in its entirety, offset should be set to 0. + /// The length parameter indicates the length of the data content. + /// This function returns the number of bytes of data successfully enqueued for NORM stream transmission. + public int Write(byte[] buffer, int offset, int length) + { + var bytes = buffer.Skip(offset).Take(length).ToArray(); + return NormStreamWrite(_handle, bytes, length); + } + + /// + /// This function allows the application to indicate to the NORM protocol engine that the last data successfully written + /// to the stream indicated by streamHandle corresponded to the end of an application-defined message boundary. + /// + public void MarkEom() + { + NormStreamMarkEom(_handle); + } + + /// + /// This function causes an immediate "flush" of the transmit stream. + /// + /// The optional eom parameter, when set to true, allows the sender application to mark an end-of-message indication + /// (see NormStreamMarkEom()) for the stream and initiate flushing in a single function call. + /// The default stream "flush" operation invoked via + /// NormStreamFlush() for flushMode equal to NORM_FLUSH_PASSIVE causes NORM to immediately transmit all + /// enqueued data for the stream(subject to session transmit rate limits), even if this results in NORM_DATA messages + /// with "small" payloads.If the optional flushMode parameter is set to NORM_FLUSH_ACTIVE, the application can + /// achieve reliable delivery of stream content up to the current write position in an even more proactive fashion.In + /// this case, the sender additionally, actively transmits NORM_CMD(FLUSH) messages after any enqueued stream + /// content has been sent. This immediately prompt receivers for repair requests which reduces latency of reliable + /// delivery, but at a cost of some additional messaging. Note any such "active" flush activity will be terminated upon + /// the next subsequent write to the stream.If flushMode is set to NORM_FLUSH_NONE, this call has no effect other than + /// the optional end-of-message marking described here. + public void Flush(bool eom, NormFlushMode flushMode) + { + NormStreamFlush(_handle, eom, flushMode); + } + + /// + /// This function causes an immediate "flush" of the transmit stream. + /// + /// + /// This is a default function which calls Flush(bool eom, NormFlushMode flushMode) override + /// with eom set as false and flushMode set to NORM_FLUSH_PASSIVE + /// + public void Flush() + { + Flush(false, NormFlushMode.NORM_FLUSH_PASSIVE); + } + + /// + /// This function halts transfer of the stream and releases any resources used unless the associated object + /// has been explicitly retained by a call to NormObjectRetain(). + /// + /// The optional graceful parameter, when + /// set to a value of true, may be used by NORM senders to initiate "graceful" shutdown of a transmit stream. + public void Close(bool graceful) + { + NormStreamClose(_handle, graceful); + } + + /// + /// This function halts transfer of the stream and releases any resources used unless the associated object + /// has been explicitly retained by a call to NormObjectRetain(). + /// + /// + /// This is a default function which calls Close(bool graceful) override + /// with graceful set as false. + /// + public void Close() + { + Close(false); + } + + /// + /// This function can be used by the receiver application to read any available data from an incoming NORM stream. + /// + /// The buffer parameter must be a pointer to an array where the received + /// data can be stored of a length as referenced by the length parameter. + /// Indicates where in the buffer to start reading the data. + /// Note: To read the data in its entirety, begin at offset 0. + /// Expected length of data received + /// The length of data received + public int Read(byte[] buffer, int offset, int length) + { + var bytes = new byte[length]; + + if (!NormStreamRead(_handle, bytes, ref length)) + { + return -1; + } + + for (var i = 0; i < length; i++) + { + var bufferPosition = offset + i; + if (bufferPosition < buffer.Length) + { + buffer[bufferPosition] = bytes[i]; + } + } + + return length; + } + + /// + /// This function advances the read offset of the receive stream referenced by the streamHandle parameter to align + /// with the next available message boundary + /// + /// + /// This function returns a value of true when start-of-message is found. The next call to NormStreamRead() will + /// retrieve data aligned with the message start. If no new message boundary is found in the buffered receive data for + /// the stream, the function returns a value of false. In this case, the application should defer repeating a call to this + /// function until a subsequent NORM_RX_OBJECT_UPDATE notification is posted. + public bool SeekMsgStart() + { + return NormStreamSeekMsgStart(_handle); + } + + /// + /// This function controls how the NORM API behaves when the application attempts to enqueue new stream data for transmission + /// when the associated stream's transmit buffer is fully occupied with data pending original or repair transmission. + /// + /// By default (pushEnable = false), a call to NormStreamWrite() will return a zero value under this + /// condition, indicating it was unable to enqueue the new data. However, if pushEnable is set to true for a given + /// streamHandle, the NORM protocol engine will discard the oldest buffered stream data(even if it is pending repair + /// transmission or has never been transmitted) as needed to enqueue the new data. + public void SetPushEnable(bool pushEnable) + { + NormStreamSetPushEnable(_handle, pushEnable); + } + + /// + /// This function sets "automated flushing" for the NORM transmit stream indicated by the streamHandle parameter. + /// + /// Possible values for the flushMode parameter include NORM_FLUSH_NONE, NORM_FLUSH_PASSIVE, and NORM_FLUSH_ACTIVE. + public void SetAutoFlush(NormFlushMode flushMode) + { + NormStreamSetAutoFlush(_handle, flushMode); + } + } +} diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Usings.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Usings.cs new file mode 100644 index 00000000..830537cc --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Usings.cs @@ -0,0 +1,3 @@ +global using static Mil.Navy.Nrl.Norm.NormApi; +global using NormEvent = Mil.Navy.Nrl.Norm.NormEvent; +global using Mil.Navy.Nrl.Norm.Enums; \ No newline at end of file diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Mil.Navy.Nrl.Norm.IntegrationTests.csproj b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Mil.Navy.Nrl.Norm.IntegrationTests.csproj new file mode 100644 index 00000000..09bff02a --- /dev/null +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Mil.Navy.Nrl.Norm.IntegrationTests.csproj @@ -0,0 +1,34 @@ + + + + net6.0 + enable + enable + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs new file mode 100644 index 00000000..e8111326 --- /dev/null +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs @@ -0,0 +1,261 @@ +using Bogus; +using System.Text; + +namespace Mil.Navy.Nrl.Norm.IntegrationTests +{ + /// + /// Tests for NORM instance + /// + public class NormInstanceTests : IDisposable + { + /// + /// The NORM instance + /// + private NormInstance _normInstance; + private NormSession? _normSession; + /// + /// Determines if the NORM instance has been destroyed + /// + private bool _isDestroyed; + + private string _testPath; + + private void CreateTestDirectory() + { + if (!Directory.Exists(_testPath)) + { + Directory.CreateDirectory(_testPath); + } + } + + private void DeleteTestDirectory() + { + if (Directory.Exists(_testPath)) + { + Directory.Delete(_testPath, true); + } + } + + /// + /// Default constructor for NORM instance tests + /// + /// + /// Creates the NORM instance. + /// Initializes _isDestroyed to false. + /// + public NormInstanceTests() + { + _normInstance = new NormInstance(); + _isDestroyed = false; + var currentDirectory = Directory.GetCurrentDirectory(); + _testPath = Path.Combine(currentDirectory, Guid.NewGuid().ToString()); + CreateTestDirectory(); + } + + /// + /// Destroy the NORM instance + /// + private void DestroyInstance() + { + if (!_isDestroyed) + { + _normSession?.DestroySession(); + _normInstance.CloseDebugLog(); + _normInstance.DestroyInstance(); + _isDestroyed = true; + } + } + + /// + /// Dispose destroys the NORM instance + /// + public void Dispose() + { + DestroyInstance(); + DeleteTestDirectory(); + } + + /// + /// Test for creating a NORM instance + /// + [Fact] + public void CreatesNormInstance() + { + Assert.NotNull(_normInstance); + } + + /// + /// Test for creating a NORM instance + /// + [Fact] + public void CreatesNormInstanceWithPriorityBoost() + { + _normInstance.DestroyInstance(); + _normInstance = new NormInstance(true); + Assert.NotNull(_normInstance); + } + + /// + /// Test for destroying a NORM instance + /// + [Fact] + public void DestroysNormInstance() + { + DestroyInstance(); + } + + /// + /// Test for creating a NORM session + /// + [Fact] + public void CreatesSession() + { + var faker = new Faker(); + var sessionAddress = "224.1.2.3"; + var sessionPort = faker.Internet.Port(); + var localNodeId = NormNode.NORM_NODE_ANY; + + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + Assert.NotNull(_normSession); + } + + /// + /// Test for throwing an exception when attempting to create a NORM session with an invalid session address + /// + [Fact] + public void CreateSessionThrowsExceptionForInvalidSessionAddress() + { + var sessionAddress = "999.999.999.999"; + var sessionPort = 6003; + var localNodeId = NormNode.NORM_NODE_ANY; + + Assert.Throws(() => _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId)); + } + + [Fact] + public void StopInstance() + { + var sessionAddress = "224.1.2.3"; + var sessionPort = 6003; + var localNodeId = NormNode.NORM_NODE_ANY; + + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + + _normInstance.StopInstance(); + } + + [Fact] + public void RestartInstance() + { + var sessionAddress = "224.1.2.3"; + var sessionPort = 6003; + var localNodeId = NormNode.NORM_NODE_ANY; + var expected = true; + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + + _normInstance.StopInstance(); + + var actual = _normInstance.RestartInstance(); + Assert.Equal(expected,actual); + } + + [Fact] + public void SuspendInstance() + { + var sessionAddress = "224.1.2.3"; + var sessionPort = 6003; + var localNodeId = NormNode.NORM_NODE_ANY; + var expected = true; + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + + _normInstance.StopInstance(); + + var actual = _normInstance.SuspendInstance(); + Assert.Equal(expected,actual); + } + [Fact] + public void ResumeInstance() + { + var sessionAddress = "224.1.2.3"; + var sessionPort = 6003; + var localNodeId = NormNode.NORM_NODE_ANY; + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + + _normInstance.StopInstance(); + + _normInstance.ResumeInstance(); + Assert.NotNull(_normInstance); + } + [Fact] + public void OpenDebugLog(){ + var fileName = Guid.NewGuid().ToString(); + var filePath = Path.Combine(_testPath, fileName); + + _normInstance.OpenDebugLog(filePath); + } + + [Fact] + public void CloseDebugLog() + { + _normInstance.CloseDebugLog(); + } + + [Fact] + public void OpensDebugPipe() + { + var pipename = "test.pipe"; + Assert.Throws(() => _normInstance.OpenDebugPipe(pipename)); + } + + [Fact] + public void SetsDebugLevel() + { + var expectedDebugLevel = 12; + _normInstance.DebugLevel = expectedDebugLevel; + var actualDebugLevel = _normInstance.DebugLevel; + Assert.Equal(expectedDebugLevel, actualDebugLevel); + } + + [Fact] + public void HasEventsFromTimeSpan() + { + var faker = new Faker(); + var sessionAddress = "224.1.2.3"; + var sessionPort = faker.Internet.Port(); + var localNodeId = NormNode.NORM_NODE_ANY; + + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + + _normSession.StartSender(1024 * 1024, 1400, 64, 16); + + var dataContent = faker.Lorem.Paragraph(); + var data = Encoding.ASCII.GetBytes(dataContent); + _normSession.DataEnqueue(data, 0, data.Length); + + Assert.True(_normInstance.HasNextEvent(TimeSpan.FromSeconds(1.5))); + + _normSession.StopSender(); + } + + [Fact] + public void HasEventsFromSecondsAndMicroseconds() + { + var faker = new Faker(); + var sessionAddress = "224.1.2.3"; + var sessionPort = faker.Internet.Port(); + var localNodeId = NormNode.NORM_NODE_ANY; + + _normSession = _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + + _normSession.StartSender(1024 * 1024, 1400, 64, 16); + + var dataContent = faker.Lorem.Paragraph(); + var data = Encoding.ASCII.GetBytes(dataContent); + _normSession.DataEnqueue(data, 0, data.Length); + + Assert.True(_normInstance.HasNextEvent(1, 500000)); + + _normSession.StopSender(); + } + } +} diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs new file mode 100644 index 00000000..9b601dd3 --- /dev/null +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs @@ -0,0 +1,2139 @@ +using Bogus; +using Mil.Navy.Nrl.Norm.Enums; +using System.Text; + +namespace Mil.Navy.Nrl.Norm.IntegrationTests +{ + public class NormSessionTests : IDisposable + { + private readonly NormInstance _normInstance; + private NormSession _normSession; + private bool _isInstanceDestroyed; + private bool _isSessionDestroyed; + private bool _isSenderStarted; + private bool _isSenderStopped; + + private bool _isReceiverStarted; + private bool _isReceiverStopped; + + private string _testPath; + + /// + /// Create a NORM session + /// + private NormSession CreateSession() + { + var faker = new Faker(); + var sessionAddress = "224.1.2.3"; + var sessionPort = faker.Internet.Port(); + var localNodeId = NormNode.NORM_NODE_ANY; + + return _normInstance.CreateSession(sessionAddress, sessionPort, localNodeId); + } + + private void CreateTestDirectory() + { + if (!Directory.Exists(_testPath)) + { + Directory.CreateDirectory(_testPath); + } + } + + private void DeleteTestDirectory() + { + if (Directory.Exists(_testPath)) + { + Directory.Delete(_testPath, true); + } + } + + public NormSessionTests() + { + _normInstance = new NormInstance(); + _normSession = CreateSession(); + _isInstanceDestroyed = false; + _isSessionDestroyed = false; + _isSenderStarted = false; + _isSenderStopped = false; + var currentDirectory = Directory.GetCurrentDirectory(); + _testPath = Path.Combine(currentDirectory, Guid.NewGuid().ToString()); + CreateTestDirectory(); + } + + private void StartSender() + { + if (!_isSenderStarted) + { + _normSession.StartSender(1024 * 1024, 1400, 64, 16); + _isSenderStarted = true; + } + } + + private void StopSender() + { + if (_isSenderStarted && !_isSenderStopped) + { + _normSession.StopSender(); + _isSenderStopped = true; + } + } + + private void StartReceiver() + { + if (!_isReceiverStarted) + { + //The appropriate bufferSpace to use is a function of expected network delay * bandwidth product and packet loss characteristics + _normSession.StartReceiver(10 * 10); + _isReceiverStarted = true; + } + } + + private void StopReceiver() + { + if (_isReceiverStarted && !_isReceiverStopped) + { + _normSession.StopReceiver(); + _isSenderStopped = true; + } + } + + /// + /// Destroys the NORM session + /// + private void DestroySession() + { + if (!_isSessionDestroyed) + { + StopSender(); + _normSession.DestroySession(); + _isSessionDestroyed = true; + } + } + + /// + /// Destroy the NORM instance + /// + private void DestroyInstance() + { + if (!_isInstanceDestroyed) + { + DestroySession(); + _normInstance.DestroyInstance(); + _isInstanceDestroyed = true; + } + } + + /// + /// Dispose destroys the NORM instance + /// + public void Dispose() + { + DestroyInstance(); + DeleteTestDirectory(); + } + + /// + /// Test for creating a NORM session + /// + [Fact] + public void CreatesSession() + { + Assert.NotNull(_normSession); + } + + /// + /// Test for destroying a NORM session + /// + [Fact] + public void DestroysSession() + { + DestroySession(); + } + + /// + /// Test for starting a NORM sender + /// + [SkippableFact(typeof(IOException))] + public void StartsSender() + { + StartSender(); + } + + /// + /// Test for stopping a NORM sender + /// + [SkippableFact(typeof(IOException))] + public void StopsSender() + { + StartSender(); + StopSender(); + } + + [SkippableFact(typeof(IOException))] + public void StartsReceiver() + { + StartReceiver(); + } + + [SkippableFact(typeof(IOException))] + public void StopsReceiver() + { + StartReceiver(); + StopReceiver(); + } + + /// + /// Generates text content + /// + /// The generated text content + private string GenerateTextContent() + { + var faker = new Faker(); + return faker.Lorem.Paragraph(); + } + + private IEnumerable GetEvents(TimeSpan delayTime) + { + var normEvents = new List(); + while (_normInstance.HasNextEvent(delayTime)) + { + var normEvent = _normInstance.GetNextEvent(false); + if (normEvent != null) + { + normEvents.Add(normEvent); + } + } + return normEvents; + } + + private IEnumerable GetEvents() + { + return GetEvents(TimeSpan.FromMilliseconds(30)); + } + + private void WaitForEvents(TimeSpan delayTime) + { + GetEvents(delayTime); + } + + private void WaitForEvents() + { + GetEvents(); + } + + private void AssertNormEvents(IEnumerable normEvents) + { + foreach (var normEvent in normEvents) + { + var normNode = normEvent.Node; + if (normNode != null) + { + var actualId = normNode.Id; + Assert.NotEqual(NormNode.NORM_NODE_NONE, actualId); + var actualAddress = normNode.Address; + Assert.NotNull(actualAddress); + var actualGrtt = normNode.Grtt; + Assert.NotEqual(-1, actualGrtt); + var expectedEventString = $"NormEvent [type={normEvent.Type}]"; + var actualEventString = normEvent.ToString(); + Assert.Equal(expectedEventString, actualEventString); + } + } + } + + [SkippableFact(typeof(IOException))] + public void EnqueuesFile() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var fileName = Guid.NewGuid().ToString(); + var filePath = Path.Combine(_testPath, fileName); + File.WriteAllText(filePath, fileContent); + + try + { + var normFile = _normSession.FileEnqueue(filePath); + Assert.NotNull(normFile); + var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; + var actualEvents = GetEvents(); + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equal(expectedEventTypes, actualEventTypes); + var expectedFileName = filePath; + var actualFileName = normFile.Name; + Assert.Equal(expectedFileName, actualFileName); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReceivesFile() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Set up file to send + var fileName = Guid.NewGuid().ToString(); + var fileContent = GenerateTextContent(); + var filePath = Path.Combine(_testPath, fileName); + File.WriteAllText(filePath, fileContent); + + try + { + //Enqueue file + var normFile = _normSession.FileEnqueue(filePath); + //Wait for events + var normEvents = GetEvents(); + AssertNormEvents(normEvents); + + var expectedNormEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; + Assert.Contains(expectedNormEventType, normEvents.Select(e => e.Type)); + var normObjectEvent = normEvents.First(e => e.Type == expectedNormEventType); + + var receivedNormFile = Assert.IsType(normObjectEvent.Object); + var expectedFileName = receivedNormFile.Name; + + //Check that file exists + var expectedFileCount = 1; + var actualFiles = Directory.GetFiles(cachePath); + var actualFileCount = actualFiles.Length; + Assert.Equal(expectedFileCount, actualFileCount); + + //Check file content + var actualFileName = actualFiles.First(); + Assert.Equal(expectedFileName, actualFileName); + var actualContent = File.ReadAllText(actualFileName); + Assert.Equal(fileContent, actualContent); + + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReceivesFileWithRename() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Set up file to send + var fileName = Guid.NewGuid().ToString(); + var fileContent = GenerateTextContent(); + var filePath = Path.Combine(_testPath, fileName); + File.WriteAllText(filePath, fileContent); + + try + { + //Enqueue file + var fileNameBytes = Encoding.ASCII.GetBytes(fileName); + var normFile = _normSession.FileEnqueue(filePath, fileNameBytes, 0, fileNameBytes.Length); + //Wait for events + var normEvents = GetEvents(); + AssertNormEvents(normEvents); + + var expectedNormEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; + Assert.Contains(expectedNormEventType, normEvents.Select(e => e.Type)); + var normObjectEvent = normEvents.First(e => e.Type == expectedNormEventType); + + var receivedNormFile = Assert.IsType(normObjectEvent.Object); + var actualInfo = receivedNormFile.Info; + Assert.NotNull(actualInfo); + + var expectedFileName = fileName; + var actualFileName = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedFileName, actualFileName); + + var expectedFilePath = Path.Combine(cachePath, actualFileName); + receivedNormFile.Rename(expectedFilePath); + + //Check that file exists + var expectedFileCount = 1; + var actualFiles = Directory.GetFiles(cachePath); + var actualFileCount = actualFiles.Length; + Assert.Equal(expectedFileCount, actualFileCount); + + //Check file content + var actualFilePath = actualFiles.First(); + Assert.Equal(expectedFilePath, actualFilePath); + var actualContent = File.ReadAllText(actualFilePath); + Assert.Equal(fileContent, actualContent); + + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void EnqueuesData() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; + var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + Assert.Equal(expectedEventTypes, actualEventTypes); + var actualData = normData.Data; + Assert.Equal(expectedData, actualData); + var actualContent = Encoding.ASCII.GetString(actualData); + Assert.Equal(expectedContent, actualContent); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReceivesData() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var expectedEventTypes = new List + { + NormEventType.NORM_REMOTE_SENDER_NEW, + NormEventType.NORM_REMOTE_SENDER_ACTIVE, + NormEventType.NORM_TX_OBJECT_SENT, + NormEventType.NORM_TX_QUEUE_EMPTY, + NormEventType.NORM_RX_OBJECT_NEW, + NormEventType.NORM_RX_OBJECT_UPDATED, + NormEventType.NORM_RX_OBJECT_COMPLETED + }; + var actualEvents = GetEvents(); + AssertNormEvents(actualEvents); + + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + + var actualData = normData.Data; + Assert.Equal(expectedData, actualData); + var actualContent = Encoding.ASCII.GetString(actualData); + Assert.Equal(expectedContent, actualContent); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void SendsStream() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + var dataOffset = 0; + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + var expectedBytesWritten = data.Length; + var actualBytesWritten = normStream.Write(data, dataOffset, data.Length); + + WaitForEvents(); + normStream.MarkEom(); + normStream.Flush(); + + Assert.Equal(expectedBytesWritten, actualBytesWritten); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReceivesStream() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + var dataOffset = 0; + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + var expectedBytesWritten = data.Length; + normStream.Write(data, dataOffset, data.Length-dataOffset); + + normStream.MarkEom(); + normStream.Flush(); + var normEvents = GetEvents(); + AssertNormEvents(normEvents); + + var expectedNormEventType = NormEventType.NORM_RX_OBJECT_UPDATED; + Assert.Contains(expectedNormEventType, normEvents.Select(e => e.Type)); + var normObjectEvent = normEvents.First(e => e.Type == expectedNormEventType); + + var receivedNormStream = Assert.IsType(normObjectEvent.Object); + var numRead = 0; + var receiveBuffer = new byte[65536]; + while ((numRead = receivedNormStream.Read(receiveBuffer, dataOffset, receiveBuffer.Length-dataOffset)) > 0) + { + if (numRead != -1) + { + var receivedData = receiveBuffer.Take(numRead).ToArray(); + var receivedContent = Encoding.ASCII.GetString(receivedData); + Assert.Equal(fileContent, receivedContent); + } + } + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReceivesStreamWithOffset() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + + normStream = _normSession.StreamOpen(repairWindowSize); + var offset = 0; + var length = data.Length; + normStream.Write(data, offset, length); + normStream.MarkEom(); + normStream.Flush(); + + var normEvents = GetEvents(); + AssertNormEvents(normEvents); + + var expectedNormEventType = NormEventType.NORM_RX_OBJECT_UPDATED; + var normObjectEvent = normEvents.First(e => e.Type == expectedNormEventType); + var receivedNormStream = Assert.IsType(normObjectEvent.Object); + + var receiveBuffer = new byte[65536]; + var receiveOffset = 0; + var receiveBufferLength = 10; + var totalNumRead = 0; + var numRead = 0; + + while ((numRead = receivedNormStream.Read(receiveBuffer, receiveOffset, receiveBufferLength)) > 0) + { + if (numRead != -1) + { + totalNumRead += numRead; + receiveOffset += numRead; + } + } + var receivedData = receiveBuffer.Take(totalNumRead).ToArray(); + var receivedContent = Encoding.ASCII.GetString(receivedData); + Assert.Equal(fileContent, receivedContent); + + + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + StopReceiver(); + } + } + + [Fact] + public void SetsTxPort() + { + _normSession.SetTxPort(6003); + } + + [Fact] + public void SetsRxPortReuseTrue() + { + _normSession.SetRxPortReuse(true); + } + + [Fact] + public void SetsRxPortReuseFalse() + { + _normSession.SetRxPortReuse(false); + } + + [Fact] + public void SetsEcnSupport() + { + _normSession.SetEcnSupport(true, true); + } + + [Fact] + public void SetsMulticastInterface() + { + _normSession.SetMulticastInterface("interface_name"); + } + + [Fact] + public void SetsSSM() + { + var sourceAddress = "224.1.2.3"; + _normSession.SetSSM(sourceAddress); + } + + [Fact] + public void SetsSSMThrowsIOException() + { + var sourceAddress = "999.999.999.999"; + Assert.Throws(() => _normSession.SetSSM(sourceAddress)); + } + + [Fact] + public void SetsTTL() + { + var ttl = (byte)200; + _normSession.SetTTL(ttl); + } + + [Fact] + public void SetsTOS() + { + var tos = (byte)200; + _normSession.SetTOS(tos); + } + + [Fact] + public void SetsMessageTrace() + { + var flag = true; + _normSession.SetMessageTrace(flag); + } + + [Fact] + public void SetsTxLoss() + { + var txLoss = .50; + _normSession.SetTxLoss(txLoss); + } + + [Fact] + public void SetsRxLoss() + { + var rxLoss = .50; + _normSession.SetRxLoss(rxLoss); + } + + [Fact] + public void SetsReportInterval() + { + var expectedReportInterval = .50; + _normSession.ReportInterval = expectedReportInterval; + var actualReportInterval = _normSession.ReportInterval; + Assert.Equal(expectedReportInterval, actualReportInterval); + } + + [Fact] + public void SetsTxOnly() + { + var txOnly = true; + _normSession.SetTxOnly(txOnly); + } + + [Fact] + public void SetsFlowControl() + { + var flowControlFactor = .50; + _normSession.SetFlowControl(flowControlFactor); + } + + [Fact] + public void SetsTxSocketBuffer() + { + StartSender(); + var bufferSize = 100; + _normSession.SetTxSocketBuffer(bufferSize); + } + + [Fact] + public void SetsCongestionControl() + { + var enable = true; + _normSession.SetCongestionControl(enable); + } + + [Fact] + public void SetsTxRateBounds() + { + var rateMin = 1.0; + var rateMax = 100.0; + _normSession.SetTxRateBounds(rateMin, rateMax); + } + + [Fact] + public void SetsTxCacheBounds() + { + var sizeMax = 100; + var countMin = 1; + var countMax = 99; + + _normSession.SetTxCacheBounds(sizeMax, countMin, countMax); + } + + [Fact] + public void SetsAutoParity() + { + short autoParity = 123; + _normSession.SetAutoParity(autoParity); + } + + [Fact] + public void SetsTxRate() + { + double txRate = 20.0; + _normSession.TxRate = txRate; + } + + [Fact] + public void GetsTxRate() + { + double expectedTxRate = 10.0; + _normSession.TxRate = expectedTxRate; + + var actualTxRate = _normSession.TxRate; + Assert.Equal(expectedTxRate, actualTxRate); + } + + [Fact] + public void SetsGrttEstimate() + { + double grttEstimate = 10.0; + _normSession.GrttEstimate = grttEstimate; + } + + [Fact] + public void GetsGrttEstimate() + { + double expetedGrttEstimate = 10.0; + _normSession.GrttEstimate = expetedGrttEstimate; + + var actualGrttEstimate = _normSession.GrttEstimate; + Assert.InRange(actualGrttEstimate, expetedGrttEstimate - 1.0, expetedGrttEstimate + 1.0); + } + + [Fact] + public void SetsGrttMax() + { + double grttMax = 20.0; + _normSession.SetGrttMax(grttMax); + } + + [Fact] + public void SetsGrttProbingMode_NORM_PROBE_NONE() + { + var probingMode = NormProbingMode.NORM_PROBE_NONE; + _normSession.SetGrttProbingMode(probingMode); + } + + [Fact] + public void SetsGrttProbingMode_NORM_PROBE_PASSIVE() + { + var probingMode = NormProbingMode.NORM_PROBE_PASSIVE; + _normSession.SetGrttProbingMode(probingMode); + } + + [Fact] + public void SetsGrttProbingMode_NORM_PROBE_ACTIVE() + { + var probingMode = NormProbingMode.NORM_PROBE_ACTIVE; + _normSession.SetGrttProbingMode(probingMode); + } + + [Fact] + public void SetsGrttProbingInterval() + { + var intervalMin = 1.0; + var intervalMax = 10.0; + _normSession.SetGrttProbingInterval(intervalMin, intervalMax); + } + + [Fact] + public void SetsBackoffFactor() + { + var backoffFactor = 10.0; + _normSession.SetBackoffFactor(backoffFactor); + } + + [Fact] + public void SetsGroupSize() + { + var groupSize = 100; + _normSession.SetGroupSize(groupSize); + } + + [Fact] + public void SetsTxRobustFactor() + { + var txRobustFactor = 2; + _normSession.SetTxRobustFactor(txRobustFactor); + } + + [SkippableFact(typeof(IOException))] + public void RequeuesObject() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; + var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + Assert.Equal(expectedEventTypes, actualEventTypes); + _normSession.RequeueObject(normData); + actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + Assert.Equal(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void SetsWatermark() + { + _normSession.AddAckingNode(_normSession.LocalNodeId); + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + _normSession.SetWatermark(normData); + var expectedEventTypes = new List + { + NormEventType.NORM_REMOTE_SENDER_NEW, + NormEventType.NORM_REMOTE_SENDER_ACTIVE, + NormEventType.NORM_TX_OBJECT_SENT, + NormEventType.NORM_TX_QUEUE_EMPTY, + NormEventType.NORM_RX_OBJECT_NEW, + NormEventType.NORM_RX_OBJECT_UPDATED, + NormEventType.NORM_RX_OBJECT_COMPLETED, + NormEventType.NORM_TX_WATERMARK_COMPLETED + }; + var actualEvents = GetEvents(TimeSpan.FromSeconds(1)); + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void CancelsWatermark() + { + _normSession.AddAckingNode(_normSession.LocalNodeId); + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + _normSession.SetWatermark(normData); + _normSession.CancelWatermark(); + var expectedEventTypes = new List + { + NormEventType.NORM_REMOTE_SENDER_NEW, + NormEventType.NORM_REMOTE_SENDER_ACTIVE, + NormEventType.NORM_TX_OBJECT_SENT, + NormEventType.NORM_TX_QUEUE_EMPTY, + NormEventType.NORM_RX_OBJECT_NEW, + NormEventType.NORM_RX_OBJECT_UPDATED, + NormEventType.NORM_RX_OBJECT_COMPLETED + }; + var actualEvents = GetEvents(TimeSpan.FromSeconds(1)); + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void ResetsWatermark() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + _normSession.SetWatermark(normData); + var expectedEventTypes = new List + { + NormEventType.NORM_REMOTE_SENDER_NEW, + NormEventType.NORM_REMOTE_SENDER_ACTIVE, + NormEventType.NORM_TX_OBJECT_SENT, + NormEventType.NORM_TX_QUEUE_EMPTY, + NormEventType.NORM_RX_OBJECT_NEW, + NormEventType.NORM_RX_OBJECT_UPDATED, + NormEventType.NORM_RX_OBJECT_COMPLETED + }; + var actualEvents = GetEvents(TimeSpan.FromSeconds(1)); + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + + _normSession.AddAckingNode(_normSession.LocalNodeId); + _normSession.ResetWatermark(); + expectedEventTypes = new List + { + NormEventType.NORM_TX_WATERMARK_COMPLETED + }; + actualEvents = GetEvents(TimeSpan.FromSeconds(1)); + actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [Fact] + public void AddsAckingNode() + { + _normSession.AddAckingNode(_normSession.LocalNodeId); + } + + [Fact] + public void RemovesAckingNode() + { + _normSession.AddAckingNode(_normSession.LocalNodeId); + _normSession.RemoveAckingNode(_normSession.LocalNodeId); + } + + [SkippableFact(typeof(IOException))] + public void GetsAckingStatus() + { + _normSession.AddAckingNode(_normSession.LocalNodeId); + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + _normSession.SetWatermark(normData); + WaitForEvents(TimeSpan.FromSeconds(1)); + var expectedAckingStatus = NormAckingStatus.NORM_ACK_SUCCESS; + var actualAckingStatus = _normSession.GetAckingStatus(_normSession.LocalNodeId); + Assert.Equal(expectedAckingStatus, actualAckingStatus); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void SendsCommand() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var expectedEventTypes = new List { NormEventType.NORM_TX_CMD_SENT }; + var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + Assert.Equal(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReceivesCommand() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var expectedEventTypes = new List + { + NormEventType.NORM_TX_CMD_SENT, + NormEventType.NORM_REMOTE_SENDER_NEW, + NormEventType.NORM_REMOTE_SENDER_ACTIVE, + NormEventType.NORM_RX_CMD_NEW + }; + var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void CancelsCommand() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.CancelCommand(); + var expectedEventTypes = new List(); + var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + Assert.Equal(expectedEventTypes, actualEventTypes); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [Fact] + public void SetsRxCacheLimit() + { + var countMax = 5; + _normSession.SetRxCacheLimit(countMax); + } + + [SkippableFact(typeof(IOException))] + public void SetsRxSocketBuffer() + { + StartSender(); + var bufferSize = 8; + _normSession.SetRxSocketBuffer(bufferSize); + } + + [Fact] + public void SetsSilentReceiver() + { + var silent = true; + _normSession.SetSilentReceiver(silent, -1); + } + + [Fact] + public void SetsDefaultUnicastNack() + { + var enable = true; + _normSession.SetDefaultUnicastNack(enable); + } + + [SkippableFact(typeof(IOException))] + public void SetsUnicastNack() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + actualNode.SetUnicastNack(true); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [Fact] + public void SetsDefaultSyncPolicy_NORM_SYNC_CURRENT() + { + var syncPolicy = NormSyncPolicy.NORM_SYNC_CURRENT; + _normSession.SetDefaultSyncPolicy(syncPolicy); + } + + [Fact] + public void SetsDefaultSyncPolicy_NORM_SYNC_STREAM() + { + var syncPolicy = NormSyncPolicy.NORM_SYNC_STREAM; + _normSession.SetDefaultSyncPolicy(syncPolicy); + } + + [Fact] + public void SetsDefaultSyncPolicy_NORM_SYNC_ALL() + { + var syncPolicy = NormSyncPolicy.NORM_SYNC_ALL; + _normSession.SetDefaultSyncPolicy(syncPolicy); + } + + [Fact] + public void SetsDefaultNackingMode_NORM_NACK_NONE() + { + var nackingMode = NormNackingMode.NORM_NACK_NONE; + _normSession.SetDefaultNackingMode(nackingMode); + } + + [Fact] + public void SetsDefaultNackingMode_NORM_NACK_INFO_ONLY() + { + var nackingMode = NormNackingMode.NORM_NACK_INFO_ONLY; + _normSession.SetDefaultNackingMode(nackingMode); + } + + [Fact] + public void SetsDefaultNackingMode_NORM_NACK_NORMAL() + { + var nackingMode = NormNackingMode.NORM_NACK_NORMAL; + _normSession.SetDefaultNackingMode(nackingMode); + } + + [SkippableFact(typeof(IOException))] + public void SetsNackingMode() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + actualNode.SetNackingMode(NormNackingMode.NORM_NACK_NONE); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [Fact] + public void SetsDefaultRepairBoundary_NORM_BOUNDARY_BLOCK() + { + var repairBoundary = NormRepairBoundary.NORM_BOUNDARY_BLOCK; + _normSession.SetDefaultRepairBoundary(repairBoundary); + } + + [Fact] + public void SetsDefaultRepairBoundary_NORM_BOUNDARY_OBJECT() + { + var repairBoundary = NormRepairBoundary.NORM_BOUNDARY_OBJECT; + _normSession.SetDefaultRepairBoundary(repairBoundary); + } + + [SkippableFact(typeof(IOException))] + public void SetsRepairBoundary() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + actualNode.SetRepairBoundary(NormRepairBoundary.NORM_BOUNDARY_OBJECT); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [Fact] + public void SetsDefaultRxRobustFactor() + { + var rxRobustFactor = 2; + _normSession.SetDefaultRxRobustFactor(rxRobustFactor); + } + + [SkippableFact(typeof(IOException))] + public void SetsRxRobustFactor() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + actualNode.SetRxRobustFactor(2); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsCommand() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + + var actualCommand = actualNode.Command; + Assert.Equal(expectedCommand, actualCommand); + var actualContent = Encoding.ASCII.GetString(actualCommand); + Assert.Equal(expectedContent, actualContent); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void FreesBuffers() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + actualNode.FreeBuffers(); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void RetainsAndReleases() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + var normEventType = NormEventType.NORM_RX_CMD_NEW; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualNode = actualEvent.Node; + Assert.NotNull(actualNode); + actualNode.Retain(); + actualNode.Release(); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsObjectType_DATA() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + Assert.Equal(NormObjectType.NORM_OBJECT_DATA, normData.Type); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsObjectType_FILE() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var fileName = Guid.NewGuid().ToString(); + var filePath = Path.Combine(_testPath, fileName); + File.WriteAllText(filePath, fileContent); + + try + { + var normFile = _normSession.FileEnqueue(filePath); + Assert.Equal(NormObjectType.NORM_OBJECT_FILE, normFile.Type); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsObjectType_STREAM() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + Assert.Equal(NormObjectType.NORM_OBJECT_STREAM, normStream.Type); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsObjectSize() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + var expectedSize = Encoding.ASCII.GetByteCount(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + Assert.Equal(expectedSize, normData.Size); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void SetsObjectNackingMode_NORM_NACK_NONE() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + normData.SetNackingMode(NormNackingMode.NORM_NACK_NONE); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void SetsObjectNackingMode_NORM_NACK_INFO_ONLY() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + normData.SetNackingMode(NormNackingMode.NORM_NACK_INFO_ONLY); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void SetsObjectNackingMode_NORM_NACK_NORMAL() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + normData.SetNackingMode(NormNackingMode.NORM_NACK_NORMAL); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsBytesPending() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + var expectedBytesPending = (long)0; + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + WaitForEvents(); + Assert.Equal(expectedBytesPending, normData.GetBytesPending()); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void CancelsObject() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + normData.Cancel(); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void RetainsObject() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + normData.Retain(); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void ReleasesObject() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + normData.Retain(); + normData.Release(); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsSenderThrowsException() + { + StartSender(); + //Create data to write to the stream + var expectedContent = GenerateTextContent(); + byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + Assert.Throws(() => normData.Sender); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsSender() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualObject = actualEvent.Object; + Assert.NotEqual(NormNode.NORM_NODE_INVALID, actualObject!.Sender); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void GetsObjectHashCode() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; + var actualEvents = GetEvents(); + Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); + var actualEvent = actualEvents.First(e => e.Type == normEventType); + var actualObject = actualEvent.Object; + var expectedHashCode = (int)actualObject!.Handle; + var actualHashCode = actualObject.GetHashCode(); + Assert.Equal(expectedHashCode, actualHashCode); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void ObjectsEqual() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + //Create data to be sent + var expectedContent = GenerateTextContent(); + var expectedData = Encoding.ASCII.GetBytes(expectedContent); + + try + { + _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + + var actualEvents = GetEvents(); + + var firstNormEventType = NormEventType.NORM_RX_OBJECT_NEW; + Assert.Contains(firstNormEventType, actualEvents.Select(e => e.Type)); + var firstEvent = actualEvents.First(e => e.Type == firstNormEventType); + var firstObject = firstEvent.Object; + Assert.NotNull(firstObject); + + var secondNormEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; + Assert.Contains(secondNormEventType, actualEvents.Select(e => e.Type)); + var secondEvent = actualEvents.First(e => e.Type == secondNormEventType); + var secondObject = secondEvent.Object; + Assert.NotNull(secondObject); + + Assert.True(firstObject.Equals(secondObject)); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamHasVacancy() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + Assert.True(normStream.HasVacancy); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamGetsReadOffset() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + var expectedReadOffset = 0; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + Assert.Equal(expectedReadOffset, normStream.ReadOffset); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamSeeksMsgStart() + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + var dataOffset = 0; + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + var expectedBytesWritten = data.Length; + normStream.Write(data, dataOffset, data.Length); + + normStream.MarkEom(); + normStream.Flush(); + var normEvents = GetEvents(); + AssertNormEvents(normEvents); + + var expectedNormEventType = NormEventType.NORM_RX_OBJECT_UPDATED; + Assert.Contains(expectedNormEventType, normEvents.Select(e => e.Type)); + var normObjectEvent = normEvents.First(e => e.Type == expectedNormEventType); + + var receivedNormStream = Assert.IsType(normObjectEvent.Object); + + Assert.True(receivedNormStream.SeekMsgStart()); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + StopReceiver(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamSetsPushEnable() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + normStream.SetPushEnable(true); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamSetsAutoFlush_FLUSH_NONE() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + normStream.SetAutoFlush(NormFlushMode.NORM_FLUSH_NONE); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamSetsAutoFlush_FLUSH_PASSIVE() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + normStream.SetAutoFlush(NormFlushMode.NORM_FLUSH_PASSIVE); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + + [SkippableFact(typeof(IOException))] + public void NormStreamSetsAutoFlush_FLUSH_ACTIVE() + { + StartSender(); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + normStream = _normSession.StreamOpen(repairWindowSize); + + normStream.SetAutoFlush(NormFlushMode.NORM_FLUSH_ACTIVE); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + } + } + } +} \ No newline at end of file diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Usings.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Usings.cs new file mode 100644 index 00000000..8c927eb7 --- /dev/null +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file From 701360ac48fa2b623535e477dd86474f228f4161 Mon Sep 17 00:00:00 2001 From: mullerj Date: Thu, 9 Mar 2023 19:37:43 -0500 Subject: [PATCH 03/29] NormSession typos in comments (#12) * Fixed default function comments in NormSession * Fixed typos in NormSession * Fixed default function comments --- .../src/Mil.Navy.Nrl.Norm/NormInstance.cs | 3 +-- .../src/Mil.Navy.Nrl.Norm/NormSession.cs | 25 ++++++++----------- .../src/Mil.Navy.Nrl.Norm/NormStream.cs | 6 ++--- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs index d8052198..f011d546 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormInstance.cs @@ -134,8 +134,7 @@ public bool HasNextEvent(TimeSpan waitTime) /// This function retrieves the next available NORM protocol event from the protocol engine. /// /// - /// This is a default function which calls GetNextEvent(bool waitForEvent) override - /// with waitForEvent set as true. + /// This is an overload which calls GetNextEvent() with waitForEvent set as true. /// /// Returns an instance of NormEvent if NormGetNextEvent() returns true, returns null otherwise. public NormEvent? GetNextEvent() diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs index de68d54c..c2fc70ad 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs @@ -105,8 +105,7 @@ private void DestroySessionNative() /// This function is used to force NORM to use a specific port number for UDP packets sent. /// /// - /// This is a default function which calls SetTxPort(int port, bool enableReuse, string? txBindAddress) override - /// with enableReuse set as false and txBindAddress set to null. + /// This is an overload which calls SetTxPort() with enableReuse set as false and txBindAddress set to null. /// /// The port parameter, specifies which port number to use. /// Thrown when NormSetTxPort() returns false, indicating the failure to set tx port. @@ -134,8 +133,7 @@ public void SetTxPort(int port, bool enableReuse, string? txBindAddress) /// This function limits the NormSession to perform NORM sender functions only. /// /// - /// This is a default function which calls etTxOnly(bool txOnly, bool connectToSessionAddress) override - /// with connectToSessionAddress set as false. + /// This is an overload which calls SetTxOnly() with connectToSessionAddress set as false. /// /// Boolean specifing whether to turn on or off the txOnly operation. public void SetTxOnly(bool txOnly) @@ -158,8 +156,7 @@ public void SetTxOnly(bool txOnly, bool connectToSessionAddress) /// This function allows the user to control the port reuse and binding behavior for the receive socket. /// /// - /// This is a default function which calls SetRxPortReuse(bool enable, string? rxBindAddress, string? senderAddress, int senderPort) - /// with rxBindAddress set to null, senderAddress set to null, and senderPort set to 0. + /// This is an overload that calls SetRxPortReuse() with rxBindAddress set to null, senderAddress set to null, and senderPort set to 0. /// /// When the enable parameter is set to true, reuse of the NormSession port number by multiple NORM instances or sessions is enabled. public void SetRxPortReuse(bool enable) @@ -181,8 +178,7 @@ public void SetRxPortReuse(bool enable, string? rxBindAddress, string? senderAdd } /// - /// This is a default function which calls SetEcnSupport(bool ecnEnable, bool ignoreLoss, bool tolerateLoss) override - /// with tolerateLoss set as false. + /// This is an overload which calls SetEcnSupport() with tolerateLoss set as false. /// /// Enables NORM ECN (congestion control) support. /// With "ecnEnable", use ECN-only, ignoring packet loss. @@ -321,8 +317,7 @@ public void SetTxSocketBuffer(long bufferSize) /// but congestion control operation can be dynamically enabled/disabled during the course of sender operation. /// /// - /// This is the default function which calls SetCongestionControl(bool enable, bool adjustRate) override - /// with adjustRate set to true. + /// This is an overload which calls SetCongestionControl() with adjustRate set to true. /// /// Specifies whether to enable or disable the NORM sender congestion control operation. public void SetCongestionControl(bool enable) @@ -464,7 +459,7 @@ public NormFile FileEnqueue(string filename) /// "repair window" as needed for some applications. While relative paths with respect to the "current working directory" /// may be used, it is recommended that full paths be used when possible. /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. - /// Indicates the strart of the message. Anything before it will not be sent. + /// Indicates the start of the message. Anything before it will not be sent. /// Note: to send full message infoOffset should be set to 0. /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. /// A NormFile is returned which the application may use in other NORM API calls as needed. @@ -496,7 +491,7 @@ public NormFile FileEnqueue(string filename, byte[] info, int infoOffset, int in /// This is an overload which will call DataEnqueue() with info set to null, infoOffset set to 0, and infoLength set to 0. /// /// The dataBuffer is a byte array containing the message to be transmitted. - /// Indicates the strart of the message. Anything before it will not be sent. + /// Indicates the start of the message. Anything before it will not be sent. /// Note: to send full message dataOffset should be set to 0. /// Size of the message. /// A NormData is returned which the application may use in other NORM API calls as needed. @@ -510,11 +505,11 @@ public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) /// This function enqueues a segment of application memory space for transmission. /// /// The dataBuffer is a byte array containing the message to be transmitted. - /// Indicates the strart of the message. Anything before it will not be sent. + /// Indicates the start of the message. Anything before it will not be sent. /// Note: to send full message dataOffset should be set to 0. /// Size of the message. /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. - /// Indicates the strart of the message. + /// Indicates the start of the message. /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. /// A NormData is returned which the application may use in other NORM API calls as needed. /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. @@ -572,7 +567,7 @@ public NormStream StreamOpen(long bufferSize) /// The bufferSize parameter controls the size of the stream's "repair window" /// which limits how far back the sender will "rewind" to satisfy receiver repair requests. /// Alloted memory space for transmitted information. - /// Indicates the strart of the message. Anything before it will not be sent. + /// Indicates the start of the message. Anything before it will not be sent. /// Note: to send full message infoOffset should be set to 0. /// Size of the message. /// A NormStream is returned which the application may use in other NORM API calls as needed. diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs index 4b443dd3..3732051d 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs @@ -83,8 +83,7 @@ public void Flush(bool eom, NormFlushMode flushMode) /// This function causes an immediate "flush" of the transmit stream. /// /// - /// This is a default function which calls Flush(bool eom, NormFlushMode flushMode) override - /// with eom set as false and flushMode set to NORM_FLUSH_PASSIVE + /// This is an overload which calls Flush() with eom set as false and flushMode set to NORM_FLUSH_PASSIVE /// public void Flush() { @@ -107,8 +106,7 @@ public void Close(bool graceful) /// has been explicitly retained by a call to NormObjectRetain(). /// /// - /// This is a default function which calls Close(bool graceful) override - /// with graceful set as false. + /// This is an overload which calls Close() with graceful set as false. /// public void Close() { From db20c644c1d828d593d95087d0da80d4c8f4feec Mon Sep 17 00:00:00 2001 From: mullerj Date: Mon, 13 Mar 2023 15:45:32 -0400 Subject: [PATCH 04/29] Feature/9 net norm extension target architecture packages (#13) * Added Win64 project and release configuration * Added project reference to Win64 project * Added Win32 project. Moved norm build actions to scripts * Added linux .NET extension project * Modified linux project to copy to output directory * Fixed linux pack script * Changed Linux project to copy always * Changed Linux project package path * Moved Windows libraries to runtimes path * Renamed runtime specific projects --- .gitattributes | 1 + .gitignore | 3 ++ README-DotNet.md | 18 +++++++ src/dotnet/Mil.Navy.Nrl.Norm.sln | 50 ++++++++++++++++++- src/dotnet/pack-linux-x64.sh | 12 +++++ src/dotnet/pack-win-x64.bat | 14 ++++++ src/dotnet/pack-win-x86.bat | 14 ++++++ .../Mil.Navy.Nrl.Norm.linux-x64.csproj | 22 ++++++++ .../Mil.Navy.Nrl.Norm.win-x64.csproj | 22 ++++++++ .../Mil.Navy.Nrl.Norm.win-x86.csproj | 22 ++++++++ 10 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 src/dotnet/pack-linux-x64.sh create mode 100644 src/dotnet/pack-win-x64.bat create mode 100644 src/dotnet/pack-win-x86.bat create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm.linux-x64/Mil.Navy.Nrl.Norm.linux-x64.csproj create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm.win-x64/Mil.Navy.Nrl.Norm.win-x64.csproj create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm.win-x86/Mil.Navy.Nrl.Norm.win-x86.csproj diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..526c8a38 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9b5994ff..73738267 100755 --- a/.gitignore +++ b/.gitignore @@ -306,3 +306,6 @@ Packages.props #Local Log Files log*.txt + +.NET Extension Library Files +/src/dotnet/lib \ No newline at end of file diff --git a/README-DotNet.md b/README-DotNet.md index d288280e..401bf05f 100644 --- a/README-DotNet.md +++ b/README-DotNet.md @@ -52,3 +52,21 @@ To package the .NET extension for NORM, execute the .NET CLI command in the src/ ``` dotnet pack . -c Release ``` + +To package the .NET extension for NORM that targets 64-bit Windows, execute the command in the src/dotnet directory: + +``` +pack-win-x64.bat +``` + +To package the .NET extension for NORM that targets 32-bit Windows, execute the command in the src/dotnet directory: + +``` +pack-win-x86.bat +``` + +To package the .NET extension for NORM that targets 64-bit Linux, execute the command in the src/dotnet directory: + +``` +./pack-linux-x64.sh +``` \ No newline at end of file diff --git a/src/dotnet/Mil.Navy.Nrl.Norm.sln b/src/dotnet/Mil.Navy.Nrl.Norm.sln index d51cd5d6..97669107 100644 --- a/src/dotnet/Mil.Navy.Nrl.Norm.sln +++ b/src/dotnet/Mil.Navy.Nrl.Norm.sln @@ -3,28 +3,71 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33213.308 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mil.Navy.Nrl.Norm", "src\Mil.Navy.Nrl.Norm\Mil.Navy.Nrl.Norm.csproj", "{0B38FE25-685E-4ADC-B63D-CA43EC023E1F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mil.Navy.Nrl.Norm", "src\Mil.Navy.Nrl.Norm\Mil.Navy.Nrl.Norm.csproj", "{0B38FE25-685E-4ADC-B63D-CA43EC023E1F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A6F15F54-6953-49E7-9394-8F6BF6EF64D0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mil.Navy.Nrl.Norm.IntegrationTests", "tests\Mil.Navy.Nrl.Norm.IntegrationTests\Mil.Navy.Nrl.Norm.IntegrationTests.csproj", "{E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mil.Navy.Nrl.Norm.IntegrationTests", "tests\Mil.Navy.Nrl.Norm.IntegrationTests\Mil.Navy.Nrl.Norm.IntegrationTests.csproj", "{E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8B6C912C-2A15-41F3-B3A4-E9C01E6672DE}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mil.Navy.Nrl.Norm.win-x64", "src\Mil.Navy.Nrl.Norm.win-x64\Mil.Navy.Nrl.Norm.win-x64.csproj", "{EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mil.Navy.Nrl.Norm.win-x86", "src\Mil.Navy.Nrl.Norm.win-x86\Mil.Navy.Nrl.Norm.win-x86.csproj", "{E5EABC55-159A-4AA2-80EC-F817C20BDA40}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CD4475A5-559A-4852-A2FE-E85A8AB9E0D8}" + ProjectSection(SolutionItems) = preProject + pack-linux.sh = pack-linux.sh + pack-win32.bat = pack-win32.bat + pack-win64.bat = pack-win64.bat + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mil.Navy.Nrl.Norm.linux-x64", "src\Mil.Navy.Nrl.Norm.linux-x64\Mil.Navy.Nrl.Norm.linux-x64.csproj", "{A4967E0C-FF3E-4B61-BD6B-D63D040585CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU + Release-linux-x64|Any CPU = Release-linux-x64|Any CPU + Release-win-x64|Any CPU = Release-win-x64|Any CPU + Release-win-x86|Any CPU = Release-win-x86|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release|Any CPU.Build.0 = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release-linux-x64|Any CPU.ActiveCfg = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release-linux-x64|Any CPU.Build.0 = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release-win-x64|Any CPU.ActiveCfg = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release-win-x64|Any CPU.Build.0 = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release-win-x86|Any CPU.ActiveCfg = Release|Any CPU + {0B38FE25-685E-4ADC-B63D-CA43EC023E1F}.Release-win-x86|Any CPU.Build.0 = Release|Any CPU {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release|Any CPU.Build.0 = Release|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release-linux-x64|Any CPU.ActiveCfg = Release|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release-win-x64|Any CPU.ActiveCfg = Release|Any CPU + {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49}.Release-win-x86|Any CPU.ActiveCfg = Release|Any CPU + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}.Release-linux-x64|Any CPU.ActiveCfg = Release|Any CPU + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}.Release-win-x64|Any CPU.ActiveCfg = Release|Any CPU + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}.Release-win-x64|Any CPU.Build.0 = Release|Any CPU + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4}.Release-win-x86|Any CPU.ActiveCfg = Release|Any CPU + {E5EABC55-159A-4AA2-80EC-F817C20BDA40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5EABC55-159A-4AA2-80EC-F817C20BDA40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5EABC55-159A-4AA2-80EC-F817C20BDA40}.Release-linux-x64|Any CPU.ActiveCfg = Release|Any CPU + {E5EABC55-159A-4AA2-80EC-F817C20BDA40}.Release-win-x64|Any CPU.ActiveCfg = Release|Any CPU + {E5EABC55-159A-4AA2-80EC-F817C20BDA40}.Release-win-x86|Any CPU.ActiveCfg = Release|Any CPU + {E5EABC55-159A-4AA2-80EC-F817C20BDA40}.Release-win-x86|Any CPU.Build.0 = Release|Any CPU + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA}.Release-linux-x64|Any CPU.ActiveCfg = Release|Any CPU + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA}.Release-linux-x64|Any CPU.Build.0 = Release|Any CPU + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA}.Release-win-x64|Any CPU.ActiveCfg = Release|Any CPU + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA}.Release-win-x86|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -32,6 +75,9 @@ Global GlobalSection(NestedProjects) = preSolution {0B38FE25-685E-4ADC-B63D-CA43EC023E1F} = {A6F15F54-6953-49E7-9394-8F6BF6EF64D0} {E5CDA4C5-C1A1-4AD7-B10D-80B8A995FC49} = {8B6C912C-2A15-41F3-B3A4-E9C01E6672DE} + {EC81BD1A-ECF0-4F4D-86E3-B0B53531EBD4} = {A6F15F54-6953-49E7-9394-8F6BF6EF64D0} + {E5EABC55-159A-4AA2-80EC-F817C20BDA40} = {A6F15F54-6953-49E7-9394-8F6BF6EF64D0} + {A4967E0C-FF3E-4B61-BD6B-D63D040585CA} = {A6F15F54-6953-49E7-9394-8F6BF6EF64D0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8BC1F27F-D633-4B45-A16D-D112CB08840E} diff --git a/src/dotnet/pack-linux-x64.sh b/src/dotnet/pack-linux-x64.sh new file mode 100644 index 00000000..3a81a2c3 --- /dev/null +++ b/src/dotnet/pack-linux-x64.sh @@ -0,0 +1,12 @@ +#!/bin/bash +currenPath="$PWD" +cd ../../ +./waf +cd "$currenPath" +libPath="$currenPath/lib" +if [ ! -d "$libPath" ] +then + mkdir "$libPath" +fi +cp -f ../../build/libnorm.so "$libPath/norm.so" +dotnet pack . -c Release-linux-x64 \ No newline at end of file diff --git a/src/dotnet/pack-win-x64.bat b/src/dotnet/pack-win-x64.bat new file mode 100644 index 00000000..fc23a99a --- /dev/null +++ b/src/dotnet/pack-win-x64.bat @@ -0,0 +1,14 @@ +set currenPath=%cd% +cd ..\..\ +python .\waf configure --msvc_target=x64 +python .\waf +cd %currenPath% +if not exist lib\ ( + mkdir lib +) +copy ..\..\build\norm*.dll lib +cd lib +del /f norm.dll +ren norm*.dll norm.dll +cd %currenPath% +dotnet pack . -c Release-win-x64 \ No newline at end of file diff --git a/src/dotnet/pack-win-x86.bat b/src/dotnet/pack-win-x86.bat new file mode 100644 index 00000000..14895dba --- /dev/null +++ b/src/dotnet/pack-win-x86.bat @@ -0,0 +1,14 @@ +set currenPath=%cd% +cd ..\..\ +python .\waf configure --msvc_target=x86 +python .\waf +cd %currenPath% +if not exist lib\ ( + mkdir lib +) +copy ..\..\build\norm*.dll lib +cd lib +del /f norm.dll +ren norm*.dll norm.dll +cd %currenPath% +dotnet pack . -c Release-win-x86 \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm.linux-x64/Mil.Navy.Nrl.Norm.linux-x64.csproj b/src/dotnet/src/Mil.Navy.Nrl.Norm.linux-x64/Mil.Navy.Nrl.Norm.linux-x64.csproj new file mode 100644 index 00000000..f3c039fc --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm.linux-x64/Mil.Navy.Nrl.Norm.linux-x64.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + 1.0.0 + + + + + Always + runtimes/linux-x64/native + true + + + + + + + + diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm.win-x64/Mil.Navy.Nrl.Norm.win-x64.csproj b/src/dotnet/src/Mil.Navy.Nrl.Norm.win-x64/Mil.Navy.Nrl.Norm.win-x64.csproj new file mode 100644 index 00000000..38a65849 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm.win-x64/Mil.Navy.Nrl.Norm.win-x64.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + 1.0.0 + + + + + Always + runtimes/win-x64/native + true + + + + + + + + diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm.win-x86/Mil.Navy.Nrl.Norm.win-x86.csproj b/src/dotnet/src/Mil.Navy.Nrl.Norm.win-x86/Mil.Navy.Nrl.Norm.win-x86.csproj new file mode 100644 index 00000000..e44f7a95 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm.win-x86/Mil.Navy.Nrl.Norm.win-x86.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + 1.0.0 + + + + + Always + runtimes/win-x86/native + true + + + + + + + + From 96476ffa79e3e61ba57d159a7af94862251f36ef Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 20 Jun 2023 20:13:00 -0400 Subject: [PATCH 05/29] Codespace Configurations (#15) * Created devcontainer.json --- .devcontainer/devcontainer.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..633f6927 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,19 @@ +{ + "image": "mcr.microsoft.com/devcontainers/cpp:1.0.0-buster", + "features": { + "ghcr.io/devcontainers/features/python:1": { + "version": "3.11" + }, + "ghcr.io/devcontainers/features/dotnet:1": { + "version": "6" + }, + "ghcr.io/devcontainers/features/java:1": { + "version": "17", + "installAnt": true, + "antVersion": "1.10.12" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "20.10" + } + } +} From a1dcbf1bc9d40b6635de798c9bf79848bd16758c Mon Sep 17 00:00:00 2001 From: mullerj Date: Thu, 29 Jun 2023 20:32:19 -0400 Subject: [PATCH 06/29] .NET norm extension GitHub actions (#16) * Added .NET action --- .github/workflows/dotnet-publish.yml | 30 ++++++++++++++++++++++++ .github/workflows/dotnet.yml | 35 ++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 .github/workflows/dotnet-publish.yml create mode 100644 .github/workflows/dotnet.yml diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml new file mode 100644 index 00000000..5c1fd3db --- /dev/null +++ b/.github/workflows/dotnet-publish.yml @@ -0,0 +1,30 @@ +name: ".NET Publish" + +on: + workflow_run: + workflows: ['.NET'] + types: + - completed + +jobs: + build: + + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/dotnet + + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Package .NET + uses: devcontainers/ci@v0.3 + with: + push: never + runCmd: | + cd ./src/dotnet + chmod 755 *.sh + ./pack-linux-x64.sh diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 00000000..836a3704 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,35 @@ +name: .NET + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/dotnet + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v3.1.4 + with: + python-version: 3.x + - name: Build NORM + run: ./waf + working-directory: . + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + - name: Restore dependencies + run: dotnet restore /property:Configuration=Release + - name: Build + run: dotnet build --no-restore --configuration Release + - name: Test + run: dotnet test --no-build --verbosity normal --configuration Release From e60a3afbde15d2eb745b436545d1fc6ad59e3251 Mon Sep 17 00:00:00 2001 From: mullerj Date: Thu, 29 Jun 2023 20:57:49 -0400 Subject: [PATCH 07/29] Update dotnet-publish.yml to use source branch (#18) --- .github/workflows/dotnet-publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml index 5c1fd3db..18c99800 100644 --- a/.github/workflows/dotnet-publish.yml +++ b/.github/workflows/dotnet-publish.yml @@ -18,6 +18,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + ref: ${{ github.event.workflow_run.head_branch }} submodules: recursive - name: Package .NET From 47bf91aee265894763784492d83e39d824b999c9 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 4 Jul 2023 10:00:15 -0400 Subject: [PATCH 08/29] .NET norm package publish (#19) * Added steps to copy package and check workspace * Changed trigger for testing * Removed if statement * Updated dotnet-publish.yml to copy to $GITHUB_OUTPUT * Changed from variable to path * Removed copy command * Modified to check runner directory * Added step to publish package * Added package write permission * Added job to publish win-x64 * Fixed package win-x64 step * Added job to publish win-x86 * Added skip-duplicate to publish commands * Added publish job * Changed to . for linux find * Added workflow trigger * Added branches filter --- .github/workflows/dotnet-publish.yml | 114 ++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml index 18c99800..981aed3b 100644 --- a/.github/workflows/dotnet-publish.yml +++ b/.github/workflows/dotnet-publish.yml @@ -5,11 +5,16 @@ on: workflows: ['.NET'] types: - completed + branches: + - master + - main jobs: - build: + publish: runs-on: ubuntu-latest + permissions: + packages: write defaults: run: working-directory: ./src/dotnet @@ -18,7 +23,33 @@ jobs: steps: - uses: actions/checkout@v3 with: - ref: ${{ github.event.workflow_run.head_branch }} + submodules: recursive + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Package .NET + run: dotnet pack . -c Release + - name: Publish .NET + run: | + package=$(find . -type f -name "*.nupkg") + dotnet nuget push "$package" --skip-duplicate + + publish-linux-x64: + + runs-on: ubuntu-latest + permissions: + packages: write + + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v3 + with: submodules: recursive - name: Package .NET @@ -29,3 +60,82 @@ jobs: cd ./src/dotnet chmod 755 *.sh ./pack-linux-x64.sh + - name: Set up .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '6.0.x' + source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Publish .NET + run: | + package=$(find . -type f -name "*linux-x64*.nupkg") + dotnet nuget push "$package" --skip-duplicate + + publish-win-x64: + + runs-on: windows-latest + permissions: + packages: write + defaults: + run: + working-directory: ./src/dotnet + + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v3.1.4 + with: + python-version: 3.x + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Package .NET + run: .\pack-win-x64.bat + - name: Publish .NET + run: | + $package=Get-ChildItem -Path .\ -Filter *win-x64*.nupkg -Recurse -File | ForEach-Object { $_.FullName } + dotnet nuget push "$package" --skip-duplicate + + publish-win-x86: + + runs-on: windows-latest + permissions: + packages: write + defaults: + run: + working-directory: ./src/dotnet + + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Setup Python + uses: actions/setup-python@v3.1.4 + with: + python-version: 3.x + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Package .NET + run: .\pack-win-x86.bat + - name: Publish .NET + run: | + $package=Get-ChildItem -Path .\ -Filter *win-x86*.nupkg -Recurse -File | ForEach-Object { $_.FullName } + dotnet nuget push "$package" --skip-duplicate From 7d7de23a6baafbb276de304be0d36716d5388cda Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 4 Jul 2023 11:12:45 -0400 Subject: [PATCH 09/29] Publish test results (#21) * Added step to publish test results * Changed upload step to use wildcard * Added quotes * Changed directory to relative path * Fixed upload step * Added step to publish test results * Added permissions --- .github/workflows/dotnet.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 836a3704..5dc76e2d 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -6,6 +6,9 @@ jobs: build: runs-on: ubuntu-latest + permissions: + checks: write + pull-requests: write defaults: run: working-directory: ./src/dotnet @@ -32,4 +35,15 @@ jobs: - name: Build run: dotnet build --no-restore --configuration Release - name: Test - run: dotnet test --no-build --verbosity normal --configuration Release + run: dotnet test --no-build --verbosity normal --configuration Release --logger trx --results-directory TestResults + - name: Upload test results + uses: actions/upload-artifact@v3 + with: + name: dotnet-results + path: ./src/dotnet/TestResults + if: ${{ always() }} + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + files: ./src/dotnet/TestResults/*.trx + if: ${{ always() }} From 2d8768c214ddadf062bb43f9b67fe65e8ea7ae51 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 4 Jul 2023 14:22:24 -0400 Subject: [PATCH 10/29] Dev Container extensions (#23) * Added extensions * Cut down on extensions * Added extension * Removed pull requests extension * Added workspace recommendation * Added powershell feature * Fixed powershell version * Updated powershell version and added extension * Updated to latest powershell --- .devcontainer/devcontainer.json | 22 +++++++++++++++++++++- .vscode/extensions.json | 5 +++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .vscode/extensions.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 633f6927..d91a01d3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -14,6 +14,26 @@ }, "ghcr.io/devcontainers/features/docker-in-docker:2": { "version": "20.10" + }, + "ghcr.io/devcontainers/features/powershell:1": { + "version": "7.3" } - } +}, +"customizations": { + "vscode": { + "extensions": [ + "DavidAnson.vscode-markdownlint", + "eamodio.gitlens", + "github.vscode-github-actions", + "GitHub.codespaces", + "jebbs.plantuml", + "ms-python.isort", + "ms-vscode-remote.remote-containers", + "ms-vscode.cpptools-themes", + "ms-vscode.PowerShell", + "tomoki1207.pdf", + "yzhang.markdown-all-in-one" + ] + } +} } diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..50bbda49 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "github.vscode-pull-request-github" + ] +} \ No newline at end of file From 2be146ceed3036d599f33a652fd948ceef6f9ee5 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 4 Jul 2023 16:50:06 -0400 Subject: [PATCH 11/29] Dev Container performance (#25) * Fixed devcontainer format * Added dotnet-linux-x64 devcontainer * Changed devcontainer image to python * Pinned OS version * Updated devcontainer base image * Reverted changes to devcontainer * Moved linux devcontainer file * Updated dotnet-publish to use subfolder --- .devcontainer/devcontainer.json | 36 +++++++++---------- .../.devcontainer/devcontainer.json | 11 ++++++ .github/workflows/dotnet-publish.yml | 1 + 3 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 .devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d91a01d3..10c74798 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,22 +18,22 @@ "ghcr.io/devcontainers/features/powershell:1": { "version": "7.3" } -}, -"customizations": { - "vscode": { - "extensions": [ - "DavidAnson.vscode-markdownlint", - "eamodio.gitlens", - "github.vscode-github-actions", - "GitHub.codespaces", - "jebbs.plantuml", - "ms-python.isort", - "ms-vscode-remote.remote-containers", - "ms-vscode.cpptools-themes", - "ms-vscode.PowerShell", - "tomoki1207.pdf", - "yzhang.markdown-all-in-one" - ] - } -} + }, + "customizations": { + "vscode": { + "extensions": [ + "DavidAnson.vscode-markdownlint", + "eamodio.gitlens", + "github.vscode-github-actions", + "GitHub.codespaces", + "jebbs.plantuml", + "ms-python.isort", + "ms-vscode-remote.remote-containers", + "ms-vscode.cpptools-themes", + "ms-vscode.PowerShell", + "tomoki1207.pdf", + "yzhang.markdown-all-in-one" + ] + } + } } diff --git a/.devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json b/.devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json new file mode 100644 index 00000000..7734dcc8 --- /dev/null +++ b/.devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json @@ -0,0 +1,11 @@ +{ + "image": "mcr.microsoft.com/devcontainers/cpp:1.0.0-buster", + "features": { + "ghcr.io/devcontainers/features/python:1": { + "version": "3.11" + }, + "ghcr.io/devcontainers/features/dotnet:1": { + "version": "6" + } + } +} diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml index 981aed3b..c6917728 100644 --- a/.github/workflows/dotnet-publish.yml +++ b/.github/workflows/dotnet-publish.yml @@ -56,6 +56,7 @@ jobs: uses: devcontainers/ci@v0.3 with: push: never + subFolder: .devcontainer/dotnet-linux-x64 runCmd: | cd ./src/dotnet chmod 755 *.sh From 43c0736d51134b7fa055cbacd49603b308d56a2e Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 4 Jul 2023 17:03:58 -0400 Subject: [PATCH 12/29] Revert "Dev Container performance (#25)" (#27) This reverts commit 2be146ceed3036d599f33a652fd948ceef6f9ee5. --- .devcontainer/devcontainer.json | 36 +++++++++---------- .../.devcontainer/devcontainer.json | 11 ------ .github/workflows/dotnet-publish.yml | 1 - 3 files changed, 18 insertions(+), 30 deletions(-) delete mode 100644 .devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 10c74798..d91a01d3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,22 +18,22 @@ "ghcr.io/devcontainers/features/powershell:1": { "version": "7.3" } - }, - "customizations": { - "vscode": { - "extensions": [ - "DavidAnson.vscode-markdownlint", - "eamodio.gitlens", - "github.vscode-github-actions", - "GitHub.codespaces", - "jebbs.plantuml", - "ms-python.isort", - "ms-vscode-remote.remote-containers", - "ms-vscode.cpptools-themes", - "ms-vscode.PowerShell", - "tomoki1207.pdf", - "yzhang.markdown-all-in-one" - ] - } - } +}, +"customizations": { + "vscode": { + "extensions": [ + "DavidAnson.vscode-markdownlint", + "eamodio.gitlens", + "github.vscode-github-actions", + "GitHub.codespaces", + "jebbs.plantuml", + "ms-python.isort", + "ms-vscode-remote.remote-containers", + "ms-vscode.cpptools-themes", + "ms-vscode.PowerShell", + "tomoki1207.pdf", + "yzhang.markdown-all-in-one" + ] + } +} } diff --git a/.devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json b/.devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json deleted file mode 100644 index 7734dcc8..00000000 --- a/.devcontainer/dotnet-linux-x64/.devcontainer/devcontainer.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "image": "mcr.microsoft.com/devcontainers/cpp:1.0.0-buster", - "features": { - "ghcr.io/devcontainers/features/python:1": { - "version": "3.11" - }, - "ghcr.io/devcontainers/features/dotnet:1": { - "version": "6" - } - } -} diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml index c6917728..981aed3b 100644 --- a/.github/workflows/dotnet-publish.yml +++ b/.github/workflows/dotnet-publish.yml @@ -56,7 +56,6 @@ jobs: uses: devcontainers/ci@v0.3 with: push: never - subFolder: .devcontainer/dotnet-linux-x64 runCmd: | cd ./src/dotnet chmod 755 *.sh From 034980eb53b558c99209ffce5fdea9e3e5de92a6 Mon Sep 17 00:00:00 2001 From: mullerj Date: Wed, 27 Mar 2024 22:11:55 -0400 Subject: [PATCH 13/29] Update action versions for dotnet-publish.yml --- .github/workflows/dotnet-publish.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml index 981aed3b..8f66cbc7 100644 --- a/.github/workflows/dotnet-publish.yml +++ b/.github/workflows/dotnet-publish.yml @@ -21,12 +21,12 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json @@ -48,7 +48,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -61,7 +61,7 @@ jobs: chmod 755 *.sh ./pack-linux-x64.sh - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: '6.0.x' source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json @@ -83,16 +83,16 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup Python - uses: actions/setup-python@v3.1.4 + uses: actions/setup-python@v5.1.0 with: python-version: 3.x - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json @@ -117,16 +117,16 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup Python - uses: actions/setup-python@v3.1.4 + uses: actions/setup-python@v5.1.0 with: python-version: 3.x - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json From b5d2e87150a5af30b2eef7e4f09188e111de9bd2 Mon Sep 17 00:00:00 2001 From: mullerj Date: Wed, 27 Mar 2024 22:18:20 -0400 Subject: [PATCH 14/29] Update action versions for dotnet.yml --- .github/workflows/dotnet.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 5dc76e2d..4bb2d85c 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -14,12 +14,12 @@ jobs: working-directory: ./src/dotnet steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Setup Python - uses: actions/setup-python@v3.1.4 + uses: actions/setup-python@v5.1.0 with: python-version: 3.x - name: Build NORM @@ -27,7 +27,7 @@ jobs: working-directory: . - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 6.0.x - name: Restore dependencies @@ -37,7 +37,7 @@ jobs: - name: Test run: dotnet test --no-build --verbosity normal --configuration Release --logger trx --results-directory TestResults - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: dotnet-results path: ./src/dotnet/TestResults From 1d91fdef333797452b4b8bba5a237c5ce958bf43 Mon Sep 17 00:00:00 2001 From: mullerj Date: Wed, 22 May 2024 18:30:54 +0000 Subject: [PATCH 15/29] Fixed ReceivesData test to use received events for actual data --- .../Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs index 9b601dd3..ba2c8bee 100644 --- a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs @@ -463,7 +463,10 @@ public void ReceivesData() var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); Assert.Equivalent(expectedEventTypes, actualEventTypes); - var actualData = normData.Data; + var actualEvent = actualEvents.FirstOrDefault(e => e.Type == NormEventType.NORM_RX_OBJECT_COMPLETED); + var actualNormData = Assert.IsType(actualEvent.Object); + + var actualData = actualNormData.Data; Assert.Equal(expectedData, actualData); var actualContent = Encoding.ASCII.GetString(actualData); Assert.Equal(expectedContent, actualContent); From 3cdb415e8ae1dfa4cce90a1719845bb61641599b Mon Sep 17 00:00:00 2001 From: mullerj Date: Mon, 27 May 2024 16:26:42 -0400 Subject: [PATCH 16/29] NormData Refactor (#30) * Switched NormData Data property to a method * Corrected NormData summary comment * Updated NormData GetData to use Marshal.Copy * Fixed warnings * Updated NormData design --- .../design/Mil/Navy/Nrl/Norm/NormData.puml | 2 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs | 22 +++++++------------ .../NormSessionTests.cs | 8 +++---- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml index 658ec091..e11e79e4 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml @@ -1,7 +1,7 @@ @startuml class NormData { ~ NormData(handle:long) - + byte[] Data <> + + byte[] GetData() } NormObject <|-- NormData @enduml \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs index bc55bc7f..a0a13e18 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormData.cs @@ -3,7 +3,7 @@ namespace Mil.Navy.Nrl.Norm { /// - /// A transport object of type NORM_OBJECT_FILE. + /// A transport object of type NORM_OBJECT_DATA. /// /// /// The data storage area for the specified transport object. @@ -11,21 +11,15 @@ namespace Mil.Navy.Nrl.Norm public class NormData : NormObject { /// - /// The data storage area associated with a transport object of type NORM_OBJECT_DATA. + /// Get the data storage area associated with a transport object of type NORM_OBJECT_DATA. /// - public byte[] Data + public byte[] GetData() { - get - { - var dataPointer = NormDataAccessData(_handle); - var length = NormObjectGetSize(_handle); - var data = new byte[length]; - for (var i = 0; i < length; i++) - { - data[i] = Marshal.ReadByte(dataPointer, i); - } - return data; - } + var dataPointer = NormDataAccessData(_handle); + var length = NormObjectGetSize(_handle); + var data = new byte[length]; + Marshal.Copy(dataPointer, data, 0, length); + return data; } /// diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs index ba2c8bee..1470c07f 100644 --- a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs @@ -93,7 +93,7 @@ private void StopReceiver() if (_isReceiverStarted && !_isReceiverStopped) { _normSession.StopReceiver(); - _isSenderStopped = true; + _isReceiverStopped = true; } } @@ -412,7 +412,7 @@ public void EnqueuesData() var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); Assert.Equal(expectedEventTypes, actualEventTypes); - var actualData = normData.Data; + var actualData = normData.GetData(); Assert.Equal(expectedData, actualData); var actualContent = Encoding.ASCII.GetString(actualData); Assert.Equal(expectedContent, actualContent); @@ -464,9 +464,9 @@ public void ReceivesData() Assert.Equivalent(expectedEventTypes, actualEventTypes); var actualEvent = actualEvents.FirstOrDefault(e => e.Type == NormEventType.NORM_RX_OBJECT_COMPLETED); - var actualNormData = Assert.IsType(actualEvent.Object); + var actualNormData = Assert.IsType(actualEvent?.Object); - var actualData = actualNormData.Data; + var actualData = actualNormData.GetData(); Assert.Equal(expectedData, actualData); var actualContent = Encoding.ASCII.GetString(actualData); Assert.Equal(expectedContent, actualContent); From edbcff7674878db87f4442a1f49ae2f4ea53f6ad Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 28 May 2024 11:50:16 -0400 Subject: [PATCH 17/29] NormDataEnqueue Refactor (#31) * Switched NormDataEnqueue to use IntPtr * Updated NormApi design --- .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 3 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 37 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index c8a210c1..c23811aa 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -59,7 +59,8 @@ class NormApi + {static} NormSetGroupSize(sessionHandle:long, groupSize:long) : void + {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void + {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long - + {static} NormDataEnqueue(sessionHandle:long, dataPtr:byte[], dataLen:int, infoPtr:byte[], infoLen:int) : long + + {static} NormDataEnqueue(sessionHandle:long, dataPtr:nint, dataLen:int, infoPtr:nint, infoLen:int) : long + + {static} NormDataEnqueue(sessionHandle:long, data:byte[], dataLen:int, info:byte[], infoLen:int) : long + {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool + {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long + {static} NormStreamClose(streamHandle:long, graceful:bool) : void diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index e8bc6db0..5e969e93 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -542,7 +542,42 @@ public struct NormEvent /// NORM_INFO content is left to the application's discretion /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. [DllImport(NORM_LIBRARY)] - public static extern long NormDataEnqueue(long sessionHandle, byte[] dataPtr, int dataLen, byte[]? infoPtr, int infoLen); + public static extern long NormDataEnqueue(long sessionHandle, nint dataPtr, int dataLen, nint infoPtr, int infoLen); + + /// + /// This function enqueues a segment of application memory space for transmission within the specified NORM sessionHandle. + /// + /// Used to identify application in the NormSession. + /// The data parameter must be a managed buffer to be transmitted. + /// The dataLen parameter indicates the quantity of data to transmit. + /// The optional info and infoLen parameters + /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen + /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the + /// NORM_INFO content is left to the application's discretion. + /// The optional info and infoLen parameters + /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen + /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the + /// NORM_INFO content is left to the application's discretion + /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. + public static long NormDataEnqueue(long sessionHandle, byte[] data, int dataLen, byte[]? info, int infoLen) + { + long objectHandle; + var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); + var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned); + + try + { + var dataPtr = dataHandle.AddrOfPinnedObject(); + var infoPtr = infoHandle.AddrOfPinnedObject(); + objectHandle = NormDataEnqueue(sessionHandle, dataPtr, dataLen, infoPtr, infoLen); + } + finally + { + dataHandle.Free(); + infoHandle.Free(); + } + return objectHandle; + } /// /// This function allows the application to resend (or reset transmission of) a NORM_OBJECT_FILE or NORM_OBJECT_DATA From b9c18c5f75a647811e57a603aaae84792c095568 Mon Sep 17 00:00:00 2001 From: mullerj Date: Wed, 5 Jun 2024 22:17:31 -0400 Subject: [PATCH 18/29] Updated GetSession to use lock --- src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs index c2fc70ad..6f240802 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs @@ -1,5 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Text; +using System.Text; namespace Mil.Navy.Nrl.Norm { @@ -75,10 +74,12 @@ internal NormSession(long handle) /// /// Specifies the session to return. /// Returns a NormSession. - [MethodImpl(MethodImplOptions.Synchronized)] internal static NormSession GetSession(long handle) { - return _normSessions[handle]; + lock (_normSessions) + { + return _normSessions[handle]; + } } /// From 0662326da80880903a54fb40de68c6c0c6245357 Mon Sep 17 00:00:00 2001 From: mullerj Date: Wed, 5 Jun 2024 22:40:09 -0400 Subject: [PATCH 19/29] Updated GetSession to TryGetValue --- src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs index 6f240802..f4b9177c 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs @@ -74,11 +74,11 @@ internal NormSession(long handle) /// /// Specifies the session to return. /// Returns a NormSession. - internal static NormSession GetSession(long handle) + internal static NormSession? GetSession(long handle) { lock (_normSessions) { - return _normSessions[handle]; + return _normSessions.TryGetValue(handle, out NormSession? session) ? session : null; } } From 662e1faf86f0161ca0a4a2130091273dc4dff2e3 Mon Sep 17 00:00:00 2001 From: mullerj Date: Thu, 13 Jun 2024 22:33:07 -0400 Subject: [PATCH 20/29] DataEnqueue Refactor (#33) * Refactored DataEnqueue to use pointer arithmetic --- .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 1 - src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 35 ---- .../src/Mil.Navy.Nrl.Norm/NormSession.cs | 48 +++-- .../NormSessionTests.cs | 188 ++++++++++++++++-- 4 files changed, 208 insertions(+), 64 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index c23811aa..027ac46e 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -60,7 +60,6 @@ class NormApi + {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void + {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long + {static} NormDataEnqueue(sessionHandle:long, dataPtr:nint, dataLen:int, infoPtr:nint, infoLen:int) : long - + {static} NormDataEnqueue(sessionHandle:long, data:byte[], dataLen:int, info:byte[], infoLen:int) : long + {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool + {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long + {static} NormStreamClose(streamHandle:long, graceful:bool) : void diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index 5e969e93..e5f56b88 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -544,41 +544,6 @@ public struct NormEvent [DllImport(NORM_LIBRARY)] public static extern long NormDataEnqueue(long sessionHandle, nint dataPtr, int dataLen, nint infoPtr, int infoLen); - /// - /// This function enqueues a segment of application memory space for transmission within the specified NORM sessionHandle. - /// - /// Used to identify application in the NormSession. - /// The data parameter must be a managed buffer to be transmitted. - /// The dataLen parameter indicates the quantity of data to transmit. - /// The optional info and infoLen parameters - /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen - /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the - /// NORM_INFO content is left to the application's discretion. - /// The optional info and infoLen parameters - /// are used to associate NORM_INFO content with the sent transport object. The maximum allowed infoLen - /// corresponds to the segmentSize used in the prior call to NormStartSender(). The use and interpretation of the - /// NORM_INFO content is left to the application's discretion - /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. - public static long NormDataEnqueue(long sessionHandle, byte[] data, int dataLen, byte[]? info, int infoLen) - { - long objectHandle; - var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); - var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned); - - try - { - var dataPtr = dataHandle.AddrOfPinnedObject(); - var infoPtr = infoHandle.AddrOfPinnedObject(); - objectHandle = NormDataEnqueue(sessionHandle, dataPtr, dataLen, infoPtr, infoLen); - } - finally - { - dataHandle.Free(); - infoHandle.Free(); - } - return objectHandle; - } - /// /// This function allows the application to resend (or reset transmission of) a NORM_OBJECT_FILE or NORM_OBJECT_DATA /// transmit object that was previously enqueued for the indicated sessionHandle. diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs index f4b9177c..b7a318bf 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Runtime.InteropServices; +using System.Text; namespace Mil.Navy.Nrl.Norm { @@ -497,6 +498,7 @@ public NormFile FileEnqueue(string filename, byte[] info, int infoOffset, int in /// Size of the message. /// A NormData is returned which the application may use in other NORM API calls as needed. /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. + /// Thrown when the data offset or data length are outside of the data buffer. public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) { return DataEnqueue(dataBuffer, dataOffset, dataLength, null, 0, 0); @@ -514,24 +516,46 @@ public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. /// A NormData is returned which the application may use in other NORM API calls as needed. /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. + /// Thrown when the data offset, data length, info offset or info length are outside of the associated buffer. public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength) { - var dataBytes = dataBuffer.Skip(dataOffset).Take(dataLength).ToArray(); - byte[]? infoBytes; - if (info != null) + if (dataOffset < 0 || dataOffset >= dataBuffer.Length) { - infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); - } - else + throw new ArgumentOutOfRangeException(nameof(dataOffset), "The data offset is out of range"); + } + if (dataOffset + dataLength > dataBuffer.Length) { - infoBytes = null; - infoLength = 0; + throw new ArgumentOutOfRangeException(nameof(dataLength), "The data length is out of range"); } - var objectHandle = NormDataEnqueue(_handle, dataBytes, dataLength, infoBytes, infoLength); - if (objectHandle == NormObject.NORM_OBJECT_INVALID) + if (infoOffset < 0 || infoOffset >= info?.Length) { - throw new IOException("Failed to enqueue data"); + throw new ArgumentOutOfRangeException(nameof(infoOffset), "The info offset is out of range"); } + if (infoOffset + infoLength > info?.Length) + { + throw new ArgumentOutOfRangeException(nameof(infoLength), "The info length is out of range"); + } + + long objectHandle; + var dataHandle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned); + var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned); + + try + { + var dataPtr = dataHandle.AddrOfPinnedObject() + dataOffset; + var infoPtr = infoHandle.AddrOfPinnedObject() + infoOffset; + objectHandle = NormDataEnqueue(_handle, dataPtr, dataLength, infoPtr, infoLength); + if (objectHandle == NormObject.NORM_OBJECT_INVALID) + { + throw new IOException("Failed to enqueue data"); + } + } + finally + { + dataHandle.Free(); + infoHandle.Free(); + } + return new NormData(objectHandle); } diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs index 1470c07f..a478299f 100644 --- a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs @@ -186,12 +186,22 @@ public void StopsReceiver() /// Generates text content /// /// The generated text content - private string GenerateTextContent() + private static string GenerateTextContent() { var faker = new Faker(); return faker.Lorem.Paragraph(); } + /// + /// Generates info content + /// + /// The generated info content + private static string GenerateInfoContent() + { + var faker = new Faker(); + return faker.Lorem.Sentence(); + } + private IEnumerable GetEvents(TimeSpan delayTime) { var normEvents = new List(); @@ -398,24 +408,85 @@ public void ReceivesFileWithRename() } } - [SkippableFact(typeof(IOException))] - public void EnqueuesData() + public static IEnumerable GenerateData() + { + var data = new List(); + + var dataContent = GenerateTextContent(); + var expectedDataContent = dataContent; + var dataOffset = 0; + var dataLength = dataContent.Length; + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); + + var infoContent = GenerateInfoContent(); + var expectedInfoContent = infoContent; + var infoOffset = 0; + var infoLength = infoContent.Length; + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); + + var faker = new Faker(); + infoLength = faker.Random.Int(infoContent.Length / 2, infoContent.Length - 1); + expectedInfoContent = infoContent.Substring(infoOffset, infoLength); + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); + + infoOffset = faker.Random.Int(1, infoContent.Length - 1 / 2); + infoLength = infoContent.Length - infoOffset; + expectedInfoContent = infoContent.Substring(infoOffset, infoLength); + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); + + infoOffset = faker.Random.Int(1, infoContent.Length - 1 / 2); + infoLength = faker.Random.Int(1, infoContent.Length - infoOffset); + expectedInfoContent = infoContent.Substring(infoOffset, infoLength); + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); + + dataLength = faker.Random.Int(dataContent.Length / 2, dataContent.Length - 1); + expectedDataContent = dataContent.Substring(dataOffset, dataLength); + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); + + dataOffset = faker.Random.Int(1, dataContent.Length - 1 / 2); + dataLength = dataContent.Length - dataOffset; + expectedDataContent = dataContent.Substring(dataOffset, dataLength); + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); + + dataOffset = faker.Random.Int(1, dataContent.Length - 1 / 2); + dataLength = faker.Random.Int(1, dataContent.Length - dataOffset); + expectedDataContent = dataContent.Substring(dataOffset, dataLength); + data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); + + return data; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateData))] + public void EnqueuesData(string dataContent, string expectedDataContent, int dataOffset, int dataLength, string? infoContent = null, string expectedInfoContent = "", int? infoOffset = null, int? infoLength = null) { StartSender(); - //Create data to write to the stream - var expectedContent = GenerateTextContent(); - byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + //Create data to write to enqueue + var data = Encoding.ASCII.GetBytes(dataContent); + var expectedData = Encoding.ASCII.GetBytes(expectedDataContent); + //Create info to enqueue + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + var expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = infoOffset != null && infoLength != null ? + _normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : + _normSession.DataEnqueue(data, dataOffset, dataLength); var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); Assert.Equal(expectedEventTypes, actualEventTypes); var actualData = normData.GetData(); Assert.Equal(expectedData, actualData); - var actualContent = Encoding.ASCII.GetString(actualData); - Assert.Equal(expectedContent, actualContent); + var actualDataContent = Encoding.ASCII.GetString(actualData); + Assert.Equal(expectedDataContent, actualDataContent); + var actualInfo = normData.Info; + Assert.Equal(expectedInfo, actualInfo); + if (actualInfo != null) + { + var actualInfoContent = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedInfoContent, actualInfoContent); + } } catch (Exception) { @@ -427,8 +498,81 @@ public void EnqueuesData() } } - [SkippableFact(typeof(IOException))] - public void ReceivesData() + public static IEnumerable GenerateOutOfRangeData() + { + var data = new List(); + + var dataContent = GenerateTextContent(); + var faker = new Faker(); + var dataOffset = faker.Random.Int(-dataContent.Length, -1); + var dataLength = dataContent.Length; + data.Add(new object[] { dataContent, dataOffset, dataLength }); + + dataOffset = faker.Random.Int(dataContent.Length, dataContent.Length * 2); + dataLength = dataContent.Length; + data.Add(new object[] { dataContent, dataOffset, dataLength }); + + dataOffset = 0; + dataLength = faker.Random.Int(dataContent.Length + 1, dataContent.Length * 2); + data.Add(new object[] { dataContent, dataOffset, dataLength }); + + dataOffset = dataContent.Length - 1; + dataLength = dataContent.Length; + data.Add(new object[] { dataContent, dataOffset, dataLength }); + + dataOffset = 0; + dataLength = dataContent.Length; + + var infoContent = GenerateInfoContent(); + var infoOffset = faker.Random.Int(-infoContent.Length, -1); + var infoLength = infoContent.Length; + data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + + infoOffset = faker.Random.Int(infoContent.Length, infoContent.Length * 2); + infoLength = infoContent.Length; + data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + + infoOffset = 0; + infoLength = faker.Random.Int(infoContent.Length + 1, infoContent.Length * 2); + data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + + infoOffset = infoContent.Length - 1; + infoLength = infoContent.Length; + data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + + return data; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateOutOfRangeData))] + public void EnqueuesDataThrowsExceptionWhenOutOfRange(string dataContent, int dataOffset, int dataLength, string? infoContent = null, int? infoOffset = null, int? infoLength = null) + { + StartSender(); + //Create data to enqueue + var data = Encoding.ASCII.GetBytes(dataContent); + //Create info to enqueue + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + + try + { + Assert.Throws(() => + infoOffset != null && infoLength != null ? + _normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : + _normSession.DataEnqueue(data, dataOffset, dataLength)); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateData))] + public void ReceivesData(string content, string expectedDataContent, int dataOffset, int dataLength, string? infoContent = null, string expectedInfoContent = "", int? infoOffset = null, int? infoLength = null) { _normSession.SetLoopback(true); StartSender(); @@ -441,12 +585,17 @@ public void ReceivesData() _normInstance.SetCacheDirectory(cachePath); //Create data to be sent - var expectedContent = GenerateTextContent(); - var expectedData = Encoding.ASCII.GetBytes(expectedContent); + var data = Encoding.ASCII.GetBytes(content); + var expectedData = Encoding.ASCII.GetBytes(expectedDataContent); + //Create info to be sent + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + var expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = infoOffset != null && infoLength != null ? + _normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : + _normSession.DataEnqueue(data, dataOffset, dataLength); var expectedEventTypes = new List { NormEventType.NORM_REMOTE_SENDER_NEW, @@ -468,8 +617,15 @@ public void ReceivesData() var actualData = actualNormData.GetData(); Assert.Equal(expectedData, actualData); - var actualContent = Encoding.ASCII.GetString(actualData); - Assert.Equal(expectedContent, actualContent); + var actualDataContent = Encoding.ASCII.GetString(actualData); + Assert.Equal(expectedDataContent, actualDataContent); + var actualInfo = normData.Info; + Assert.Equal(expectedInfo, actualInfo); + if (actualInfo != null) + { + var actualInfoContent = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedInfoContent, actualInfoContent); + } } catch (Exception) { From d539a35a73099ca51d28027644ae5b9efeea9950 Mon Sep 17 00:00:00 2001 From: mullerj Date: Mon, 29 Jul 2024 08:58:08 -0400 Subject: [PATCH 21/29] NormDataEnqueue Refactor (#34) * Updated to use SafeBuffer * Added ByteBuffer and DirectByteBuffer * Added pointer overload to DataEnqueue * Updated FileEnqueue() to use pointer arithmetic * Updated NormStream to use pointer arithmetic * Updated NormSendCommand to nint * Updated NormNodeGetCommand to nint * Updated NormNodeGetAddress to use nint for buffer * Updated NormStreamRead to use nint for buffer * Updated NormObjectGetInfo to use nint for buffer * Updated NormFileGetName nameBuffer to nint --- .../Mil/Navy/Nrl/Norm/Buffers/ByteBuffer.puml | 7 + .../Nrl/Norm/Buffers/DirectByteBuffer.puml | 7 + .../Navy/Nrl/Norm/Enums/NormAckingStatus.puml | 10 +- .../Navy/Nrl/Norm/Enums/NormEventType.puml | 60 +- .../Mil/Navy/Nrl/Norm/Enums/NormFecType.puml | 8 +- .../Navy/Nrl/Norm/Enums/NormFlushMode.puml | 8 +- .../Navy/Nrl/Norm/Enums/NormNackingMode.puml | 8 +- .../Navy/Nrl/Norm/Enums/NormObjectType.puml | 10 +- .../Navy/Nrl/Norm/Enums/NormProbingMode.puml | 8 +- .../Nrl/Norm/Enums/NormRepairBoundary.puml | 6 +- .../Navy/Nrl/Norm/Enums/NormSyncPolicy.puml | 8 +- .../Navy/Nrl/Norm/IO/INormEventListener.puml | 2 +- .../Mil/Navy/Nrl/Norm/IO/NormInputStream.puml | 25 +- .../Navy/Nrl/Norm/IO/NormOutputStream.puml | 26 +- .../Nrl/Norm/IO/StreamBreakException.puml | 4 +- .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 230 +++--- .../design/Mil/Navy/Nrl/Norm/NormData.puml | 6 +- .../design/Mil/Navy/Nrl/Norm/NormEvent.puml | 18 +- .../design/Mil/Navy/Nrl/Norm/NormFile.puml | 6 +- .../Mil/Navy/Nrl/Norm/NormInstance.puml | 22 +- .../design/Mil/Navy/Nrl/Norm/NormNode.puml | 14 +- .../design/Mil/Navy/Nrl/Norm/NormObject.puml | 15 +- .../design/Mil/Navy/Nrl/Norm/NormSession.puml | 75 +- .../design/Mil/Navy/Nrl/Norm/NormStream.puml | 20 +- .../Mil.Navy.Nrl.Norm/Buffers/ByteBuffer.cs | 26 + .../Buffers/DirectByteBuffer.cs | 30 + .../Mil.Navy.Nrl.Norm.csproj | 1 + src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 23 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs | 24 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs | 54 +- .../src/Mil.Navy.Nrl.Norm/NormObject.cs | 18 +- .../src/Mil.Navy.Nrl.Norm/NormSession.cs | 145 +++- .../src/Mil.Navy.Nrl.Norm/NormStream.cs | 57 +- .../NormInstanceTests.cs | 13 +- .../NormSessionTests.cs | 662 ++++++++++++++++-- 35 files changed, 1214 insertions(+), 442 deletions(-) create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/ByteBuffer.puml create mode 100644 src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/DirectByteBuffer.puml create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/ByteBuffer.cs create mode 100644 src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/DirectByteBuffer.cs diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/ByteBuffer.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/ByteBuffer.puml new file mode 100644 index 00000000..22de7e71 --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/ByteBuffer.puml @@ -0,0 +1,7 @@ +@startuml +abstract class ByteBuffer { + # ByteBuffer() + + {static} AllocateDirect(capacity:int) : ByteBuffer +} +SafeBuffer <|-- ByteBuffer +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/DirectByteBuffer.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/DirectByteBuffer.puml new file mode 100644 index 00000000..58106fca --- /dev/null +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Buffers/DirectByteBuffer.puml @@ -0,0 +1,7 @@ +@startuml +class DirectByteBuffer <> { + <> DirectByteBuffer(capacity:int) + # <> ReleaseHandle() : bool +} +ByteBuffer <|-- DirectByteBuffer +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml index f31c80ee..aa34be5f 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormAckingStatus.puml @@ -1,8 +1,8 @@ @startuml enum NormAckingStatus { - NORM_ACK_INVALID - NORM_ACK_FAILURE - NORM_ACK_PENDING - NORM_ACK_SUCCESS + NORM_ACK_INVALID, + NORM_ACK_FAILURE, + NORM_ACK_PENDING, + NORM_ACK_SUCCESS, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml index 71dd8fb2..bf72c92d 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormEventType.puml @@ -1,33 +1,33 @@ @startuml enum NormEventType { - NORM_EVENT_INVALID - NORM_TX_QUEUE_VACANCY - NORM_TX_QUEUE_EMPTY - NORM_TX_FLUSH_COMPLETED - NORM_TX_WATERMARK_COMPLETED - NORM_TX_CMD_SENT - NORM_TX_OBJECT_SENT - NORM_TX_OBJECT_PURGED - NORM_TX_RATE_CHANGED - NORM_LOCAL_SENDER_CLOSED - NORM_REMOTE_SENDER_NEW - NORM_REMOTE_SENDER_RESET - NORM_REMOTE_SENDER_ADDRESS - NORM_REMOTE_SENDER_ACTIVE - NORM_REMOTE_SENDER_INACTIVE - NORM_REMOTE_SENDER_PURGED - NORM_RX_CMD_NEW - NORM_RX_OBJECT_NEW - NORM_RX_OBJECT_INFO - NORM_RX_OBJECT_UPDATED - NORM_RX_OBJECT_COMPLETED - NORM_RX_OBJECT_ABORTED - NORM_RX_ACK_REQUEST - NORM_GRTT_UPDATED - NORM_CC_ACTIVE - NORM_CC_INACTIVE - NORM_ACKING_NODE_NEW - NORM_SEND_ERROR - NORM_USER_TIMEOUT + NORM_EVENT_INVALID, + NORM_TX_QUEUE_VACANCY, + NORM_TX_QUEUE_EMPTY, + NORM_TX_FLUSH_COMPLETED, + NORM_TX_WATERMARK_COMPLETED, + NORM_TX_CMD_SENT, + NORM_TX_OBJECT_SENT, + NORM_TX_OBJECT_PURGED, + NORM_TX_RATE_CHANGED, + NORM_LOCAL_SENDER_CLOSED, + NORM_REMOTE_SENDER_NEW, + NORM_REMOTE_SENDER_RESET, + NORM_REMOTE_SENDER_ADDRESS, + NORM_REMOTE_SENDER_ACTIVE, + NORM_REMOTE_SENDER_INACTIVE, + NORM_REMOTE_SENDER_PURGED, + NORM_RX_CMD_NEW, + NORM_RX_OBJECT_NEW, + NORM_RX_OBJECT_INFO, + NORM_RX_OBJECT_UPDATED, + NORM_RX_OBJECT_COMPLETED, + NORM_RX_OBJECT_ABORTED, + NORM_RX_ACK_REQUEST, + NORM_GRTT_UPDATED, + NORM_CC_ACTIVE, + NORM_CC_INACTIVE, + NORM_ACKING_NODE_NEW, + NORM_SEND_ERROR, + NORM_USER_TIMEOUT, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml index 4b55669b..c492e798 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFecType.puml @@ -1,7 +1,7 @@ @startuml enum NormFecType { - RS - RS8 - SB + RS, + RS8, + SB, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml index 63f7b1a8..1de4e631 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormFlushMode.puml @@ -1,7 +1,7 @@ @startuml enum NormFlushMode { - NORM_FLUSH_NONE - NORM_FLUSH_PASSIVE - NORM_FLUSH_ACTIVE + NORM_FLUSH_NONE, + NORM_FLUSH_PASSIVE, + NORM_FLUSH_ACTIVE, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml index 357ea0b2..81285969 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormNackingMode.puml @@ -1,7 +1,7 @@ @startuml enum NormNackingMode { - NORM_NACK_NONE - NORM_NACK_INFO_ONLY - NORM_NACK_NORMAL + NORM_NACK_NONE, + NORM_NACK_INFO_ONLY, + NORM_NACK_NORMAL, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml index 00e2dd84..42689868 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormObjectType.puml @@ -1,8 +1,8 @@ @startuml enum NormObjectType { - NORM_OBJECT_NONE - NORM_OBJECT_DATA - NORM_OBJECT_FILE - NORM_OBJECT_STREAM + NORM_OBJECT_NONE, + NORM_OBJECT_DATA, + NORM_OBJECT_FILE, + NORM_OBJECT_STREAM, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml index 004ada72..93c95f00 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormProbingMode.puml @@ -1,7 +1,7 @@ @startuml enum NormProbingMode { - NORM_PROBE_NONE - NORM_PROBE_PASSIVE - NORM_PROBE_ACTIVE + NORM_PROBE_NONE, + NORM_PROBE_PASSIVE, + NORM_PROBE_ACTIVE, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml index b97df403..bba6130f 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormRepairBoundary.puml @@ -1,6 +1,6 @@ @startuml enum NormRepairBoundary { - NORM_BOUNDARY_BLOCK - NORM_BOUNDARY_OBJECT + NORM_BOUNDARY_BLOCK, + NORM_BOUNDARY_OBJECT, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml index 3bb1cf54..870db564 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/Enums/NormSyncPolicy.puml @@ -1,7 +1,7 @@ @startuml enum NormSyncPolicy { - NORM_SYNC_CURRENT - NORM_SYNC_STREAM - NORM_SYNC_ALL + NORM_SYNC_CURRENT, + NORM_SYNC_STREAM, + NORM_SYNC_ALL, } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml index ba923b3a..0243b8e8 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/INormEventListener.puml @@ -2,4 +2,4 @@ interface INormEventListener { NormEventOccurred(normEvent:NormEvent) : void } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml index c6cfef6a..9136cba3 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormInputStream.puml @@ -1,32 +1,30 @@ @startuml class NormInputStream { - - _normInstance : NormInstance - - _normSession : NormSession - - _normStream : NormStream - - _normEventListeners : List + - _normStream : NormStream? - _closed : bool - _closeLock : object - _bufferIsEmpty : bool - _receivedEof : bool + NormInputStream(address:string, port:int) - + OpenDebugLog(filename:string) : void - + SetDebugLevel(level:int) : void + + OpenDebugLog(fileName:string) : void + + CloseDebugLog() : void + + NormSetDebugLevel(level:int) : void + SetMessageTrace(messageTrace:bool) : void - + SetMulticastInterface(multicastInterface: string) : void - + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool) + + SetMulticastInterface(multicastInterface:string) : void + + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool) : void + SetTtl(ttl:byte) : void + SetTos(tos:byte) : void - + SetSilentReceiver(silent:bool, maxDelay:int) : void + + setSilentReceiver(silent:bool, maxDelay:int) : void + SetDefaultUnicastNack(defaultUnicastNack:bool) : void + SeekMsgStart() : void + AddNormEventListener(normEventListener:INormEventListener) : void + RemoveNormEventListener(normEventListener:INormEventListener) : void - FireNormEventOccured(normEvent:NormEvent) : void + Open(bufferSpace:long) : void - + <> Dispose() : void + + <> Close() : void + IsClosed : bool <> + Read() : int - + <> Read(buffer:byte[], offset:int, length:int) : int + + <> Read(buffer:byte[], offset:int, count:int) : int - ProcessEvent() : void + <> Flush() : void + <> Write(buffer:byte[], offset:int, count:int) : void @@ -38,5 +36,10 @@ class NormInputStream { + <> Length : long <> + <> Position : long <> <> } +class "List`1" { +} Stream <|-- NormInputStream +NormInputStream --> "_normInstance" NormInstance +NormInputStream --> "_normSession" NormSession +NormInputStream --> "_normEventListeners" "List`1" @enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml index 72468b11..a15b0e63 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/NormOutputStream.puml @@ -1,9 +1,6 @@ @startuml class NormOutputStream { - - _normInstance : NormInstance - - _normSession : NormSession - - _normStream : NormStream - - _normEventListeners : List + - _normStream : NormStream? - _closed : bool - _closeLock : object - _bufferIsFull : bool @@ -13,13 +10,13 @@ class NormOutputStream { + SetDebugLevel(level:int) : void + SetMessageTrace(messageTrace:bool) : void + SetMulticastInterface(multicastInterface:string) : void - + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool): void + + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool) : void + SetTtl(ttl:byte) : void + SetTos(tos:byte) : void - + SetCongestionControl(ccEnabled:bool, ccAdjustRate: bool) : void + + SetCongestionControl(ccEnabled:bool, ccAdjustRate:bool) : void + SetTxRateBounds(minTxRate:double, maxTxRate:double) : void - + TxRate:double <> <> - + GrttEstimate:double <> <> + + TxRate : double <> <> + + GrttEstimate : double <> <> + SetGroupSize(groupSize:long) : void + SetAutoParity(autoParity:short) : void + SetBackoffFactor(backoffFactor:double) : void @@ -27,16 +24,16 @@ class NormOutputStream { + SetPushEnable(pushEnable:bool) : void + MarkEom() : void + AddNormEventListener(normEventListener:INormEventListener) : void - + RemoveNormEventListener(normEventListener:INormEventListener): void + + RemoveNormEventListener(normEventListener:INormEventListener) : void - FireNormEventOccured(normEvent:NormEvent) : void + Open(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, repairWindow:long) : void + <> Close() : void + IsClosed : bool <> + Write(b:int) : void - + <> Write(buffer:byte[], offset:int, count:int) + + <> Write(buffer:byte[], offset:int, count:int) : void - ProcessEvent() : void + <> Flush() : void - + <> Read(buffer:byte[], offset:int, length:int) : int + + <> Read(buffer:byte[], offset:int, count:int) : int + <> Seek(offset:long, origin:SeekOrigin) : long + <> SetLength(value:long) : void + <> CanRead : bool <> @@ -45,5 +42,10 @@ class NormOutputStream { + <> Length : long <> + <> Position : long <> <> } +class "List`1" { +} Stream <|-- NormOutputStream -@enduml \ No newline at end of file +NormOutputStream --> "_normInstance" NormInstance +NormOutputStream --> "_normSession" NormSession +NormOutputStream --> "_normEventListeners" "List`1" +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml index 6ae40082..10cdc78f 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/IO/StreamBreakException.puml @@ -1,6 +1,6 @@ @startuml class StreamBreakException { - + StreamBreakException(message:string) + + StreamBreakException(message:string?) } IOException <|-- StreamBreakException -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index 027ac46e..824d07a8 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -1,119 +1,119 @@ -@startuml -struct NormEvent -{ - + Type : NormEventType +@startuml +class NormApi <> { + + <> NORM_LIBRARY : string = "norm" + + {static} <> NormCreateInstance(priorityBoost:bool) : long + + {static} <> NormDestroyInstance(instanceHandle:long) : void + + {static} <> NormStopInstance(instanceHandle:long) : void + + {static} <> NormRestartInstance(instanceHandle:long) : bool + + {static} <> NormSuspendInstance(instanceHandle:long) : bool + + {static} <> NormResumeInstance(instanceHandle:long) : bool + + {static} <> NormSetCacheDirectory(instanceHandle:long, cachePath:string) : bool + + {static} <> NormGetNextEvent(instanceHandle:long, theEvent:NormEvent, waitForEvent:bool) : bool + + {static} <> NormGetDescriptor(instanceHandle:long) : int + + {static} <> NormCreateSession(instanceHandle:long, sessionAddress:string, sessionPort:int, localNodeId:long) : long + + {static} <> NormDestroySession(sessionHandle:long) : void + + {static} <> NormGetLocalNodeId(sessionHandle:long) : long + + {static} <> NormSetTxPort(sessionHandle:long, txPortNumber:int, enableReuse:bool, txBindAddress:string?) : bool + + {static} <> NormSetTxOnly(sessionHandle:long, txOnly:bool, connectToSessionAddress:bool) : void + + {static} <> NormSetRxPortReuse(sessionHandle:long, enableReuse:bool, rxBindAddress:string?, senderAddress:string?, senderPort:int) : void + + {static} <> NormSetEcnSupport(sessionHandle:long, ecnEnable:bool, ignoreLoss:bool, tolerateLoss:bool) : void + + {static} <> NormSetMulticastInterface(sessionHandle:long, interfaceName:string) : bool + + {static} <> NormSetSSM(sessionHandle:long, sourceAddress:string) : bool + + {static} <> NormSetTTL(sessionHandle:long, ttl:byte) : bool + + {static} <> NormSetTOS(sessionHandle:long, tos:byte) : bool + + {static} <> NormSetLoopback(sessionHandle:long, loopback:bool) : bool + + {static} <> NormSetMessageTrace(sessionHandle:long, flag:bool) : void + + {static} <> NormSetTxLoss(sessionHandle:long, precent:double) : void + + {static} <> NormSetRxLoss(sessionHandle:long, precent:double) : void + + {static} <> NormOpenDebugLog(instanceHandle:long, path:string) : bool + + {static} <> NormCloseDebugLog(instanceHandle:long) : bool + + {static} <> NormOpenDebugPipe(instanceHandle:long, pipeName:string) : bool + + {static} <> NormSetDebugLevel(level:int) : void + + {static} <> NormGetDebugLevel() : int + + {static} <> NormSetReportInterval(sessionHandle:long, interval:double) : void + + {static} <> NormGetReportInterval(sessionHandle:long) : double + + {static} <> NormGetRandomSessionId() : int + + {static} <> NormStartSender(instanceHandle:long, instanceId:int, bufferSpace:long, segmentSize:int, numData:short, numParity:short, fecId:NormFecType) : bool + + {static} <> NormStopSender(sessionHandle:long) : void + + {static} <> NormSetTxRate(sessionHandle:long, rate:double) : void + + {static} <> NormGetTxRate(sessionHandle:long) : double + + {static} <> NormSetTxSocketBuffer(sessionHandle:long, bufferSize:long) : bool + + {static} <> NormSetFlowControl(sessionHandle:long, flowControlFactor:double) : void + + {static} <> NormSetCongestionControl(sessionHandle:long, enable:bool, adjustRate:bool) : void + + {static} <> NormSetTxRateBounds(sessionHandle:long, rateMin:double, rateMax:double) : void + + {static} <> NormSetTxCacheBounds(sessionHandle:long, sizeMax:long, countMin:long, countMax:long) : void + + {static} <> NormSetAutoParity(sesssionHandle:long, autoParity:short) : void + + {static} <> NormSetGrttEstimate(sessionHandle:long, grtt:double) : void + + {static} <> NormGetGrttEstimate(sessionHandle:long) : double + + {static} <> NormSetGrttMax(sessionHandle:long, grttMax:double) : void + + {static} <> NormSetGrttProbingMode(sesssionHandle:long, probingMode:NormProbingMode) : void + + {static} <> NormSetGrttProbingInterval(sessionHandle:long, intervalMin:double, intervalMax:double) : void + + {static} <> NormSetBackoffFactor(sessionHandle:long, backoffFactor:double) : void + + {static} <> NormSetGroupSize(sessionHandle:long, groupSize:long) : void + + {static} <> NormSetTxRobustFactor(sessionHandle:long, txRobustFactor:int) : void + + {static} <> NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:nint, infoLen:int) : long + + {static} <> NormDataEnqueue(sessionHandle:long, dataPtr:nint, dataLen:int, infoPtr:nint, infoLen:int) : long + + {static} <> NormRequeueObject(sessionHandle:long, objectHandle:long) : bool + + {static} <> NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:nint, infoLen:int) : long + + {static} <> NormStreamClose(streamHandle:long, graceful:bool) : void + <> {static} <> NormStreamWrite(streamHandle:long, buffer:nint, numBytes:int) : int + + {static} <> NormStreamFlush(streamHandle:long, eom:bool, flushMode:NormFlushMode) : void + + {static} <> NormStreamSetAutoFlush(streamHandle:long, flushMode:NormFlushMode) : void + + {static} <> NormStreamSetPushEnable(streamHandle:long, pushEnable:bool) : void + + {static} <> NormStreamHasVacancy(streamHandle:long) : bool + + {static} <> NormStreamMarkEom(streamHandle:long) : void + + {static} <> NormSetWatermark(sessionHandle:long, objectHandle:long, overrideFlush:bool) : bool + + {static} <> NormResetWatermark(sessionHandle:long) : bool + + {static} <> NormCancelWatermark(sessionHandle:long) : void + + {static} <> NormAddAckingNode(sessionHandle:long, nodeId:long) : bool + + {static} <> NormRemoveAckingNode(sessionHandle:long, nodeId:long) : void + + {static} <> NormGetAckingStatus(sessionHandle:long, nodeId:long) : NormAckingStatus + + {static} <> NormSendCommand(sessionHandle:long, cmdBuffer:nint, cmdLength:int, robust:bool) : bool + + {static} <> NormCancelCommand(sessionHandle:long) : void + + {static} <> NormStartReceiver(sessionHandle:long, bufferSpace:long) : bool + + {static} <> NormStopReceiver(sessionHandle:long) : void + + {static} <> NormSetRxCacheLimit(sessionHandle:long, countMax:int) : void + + {static} <> NormSetRxSocketBuffer(sessionHandle:long, bufferSize:long) : bool + + {static} <> NormSetSilentReceiver(sessionHandle:long, silent:bool, maxDelay:int) : void + + {static} <> NormSetDefaultUnicastNack(sessionHandle:long, unicastNacks:bool) : void + + {static} <> NormNodeSetUnicastNack(remoteSender:long, unicastNacks:bool) : void + + {static} <> NormSetDefaultSyncPolicy(sessionHandle:long, syncPolicy:NormSyncPolicy) : void + + {static} <> NormSetDefaultNackingMode(sessionHandle:long, nackingMode:NormNackingMode) : void + + {static} <> NormNodeSetNackingMode(remoteSender:long, nackingMode:NormNackingMode) : void + + {static} <> NormObjectSetNackingMode(objectHandle:long, nackingMode:NormNackingMode) : void + + {static} <> NormSetDefaultRepairBoundary(sessionHandle:long, repairBoundary:NormRepairBoundary) : void + + {static} <> NormNodeSetRepairBoundary(remoteSender:long, repairBoundary:NormRepairBoundary) : void + + {static} <> NormSetDefaultRxRobustFactor(sessionHandle:long, robustFactor:int) : void + + {static} <> NormNodeSetRxRobustFactor(remoteSender:long, robustFactor:int) : void + + {static} <> NormStreamRead(streamHandle:long, buffer:nint, numBytes:int) : bool + + {static} <> NormStreamSeekMsgStart(streamHandle:long) : bool + + {static} <> NormStreamGetReadOffset(streamHandle:long) : long + + {static} <> NormObjectGetType(objectHandle:long) : NormObjectType + + {static} <> NormObjectHasInfo(objectHandle:long) : bool + + {static} <> NormObjectGetInfoLength(objectHandle:long) : int + + {static} <> NormObjectGetInfo(objectHandle:long, buffer:nint, bufferLen:int) : int + + {static} <> NormObjectGetSize(objectHandle:long) : int + + {static} <> NormObjectGetBytesPending(objectHandle:long) : long + + {static} <> NormObjectCancel(objectHandle:long) : void + + {static} <> NormObjectRetain(objectHandle:long) : void + + {static} <> NormObjectRelease(objectHandle:long) : void + + {static} <> NormFileGetName(fileHandle:long, nameBuffer:nint, bufferLen:int) : bool + + {static} <> NormFileRename(fileHandle:long, fileName:string) : bool + + {static} <> NormDataAccessData(objectHandle:long) : nint + + {static} <> NormObjectGetSender(objectHandle:long) : long + + {static} <> NormNodeGetId(nodeHandle:long) : long + + {static} <> NormNodeGetAddress(nodeHandle:long, addrBuffer:nint, bufferLen:int, port:int) : bool + + {static} <> NormNodeGetGrtt(nodeHandle:long) : double + + {static} <> NormNodeGetCommand(remoteSender:long, cmdBuffer:nint, buflen:int) : bool + + {static} <> NormNodeFreeBuffers(remoteSender:long) : void + + {static} <> NormNodeRetain(nodeHandle:long) : void + + {static} <> NormNodeRelease(nodeHandle:long) : void +} +struct NormEvent { + Session : long + Sender : long + Object : long } -class NormApi -{ - + <> NORM_LIBRARY : string = "norm" - + {static} NormCreateInstance(priorityBoost:bool) : long - + {static} NormDestroyInstance(instanceHandle:long) : void - + {static} NormStopInstance(instanceHandle:long) : void - + {static} NormRestartInstance(instanceHandle:long) : bool - + {static} NormSuspendInstance(instanceHandle:long) : bool - + {static} NormResumeInstance(instanceHandle:long) : void - + {static} NormSetCacheDirectory(instanceHandle:long, cachePath:string) : bool - + {static} NormGetNextEvent(instanceHandle:long, theEvent:NormEvent, waitForEvent:bool) : bool - + {static} NormGetDescriptor(instanceHandle:long) : int - + {static} NormCreateSession(instanceHandle:long, sessionAddress:string, sessionPort:int, localNodeId:long) : long - + {static} NormDestroySession(sessionHandle:long) : void - + {static} NormGetLocalNodeId(sessionHandle:long) : long - + {static} NormSetTxPort(sessionHandle:long, txPortNumber:int, enableReuse:bool, txBindAddress:string) : bool - + {static} NormSetTxOnly(sessionHandle:long, txOnly:bool, connectToSessionAddress:bool) : void - + {static} NormSetRxPortReuse(sessionHandle:long, enableReuse:bool, rxBindAddress:string, senderAddress:string, senderPort:int) : void - + {static} NormSetEcnSupport(sessionHandle:long, ecnEnable:bool, ignoreLoss:bool, tolerateLoss:bool) : void - + {static} NormSetMulticastInterface(sessionHandle:long, interfaceName:string) : bool - + {static} NormSetSSM(sessionHandle:long, sourceAddress:string) : bool - + {static} NormSetTTL(sessionHandle:long, ttl:byte) : bool - + {static} NormSetTOS(sessionHandle:long, tos:byte) : bool - + {static} NormSetLoopback(sessionHandle:long, loopback:bool) : bool - + {static} NormSetMessageTrace(sessionHandle:long, state:bool) : void - + {static} NormSetTxLoss(sessionHandle:long, precent:double) : void - + {static} NormSetRxLoss(sessionHandle:long, precent:double) : void - + {static} NormOpenDebugLog(instanceHandle:long, path:string) : bool - + {static} NormCloseDebugLog(instanceHandle:long) : void - + {static} NormOpenDebugPipe(instanceHandle:long, pipeName:string) : bool - + {static} NormSetDebugLevel(level:int) : void - + {static} NormGetDebugLevel() : int - + {static} NormSetReportInterval(sessionHandle:long, interval:double) : void - + {static} NormGetRandomSessionId() : int - + {static} NormStartSender(sessionHandle:long, instanceId:int, bufferSpace:long, segmentSize:int, numData:short, numParity:short, fecId:NormFecType) : bool - + {static} NormStopSender(sessionHandle:long) : void - + {static} NormSetTxRate(sessionHandle:long, bitsPerSecond:double) : void - + {static} NormGetTxRate(sessionHandle:long) : double - + {static} NormSetTxSocketBuffer(sessionHandle:long, bufferSize:long) : bool - + {static} NormSetFlowControl(sessionHandle:long, flowControlFactor:double) : void - + {static} NormSetCongestionControl(sessionHandle:long, enable:bool, adjustRate:bool) : void - + {static} NormSetTxRateBounds(sessionHandle:long, rateMin:double, rateMax:double) : void - + {static} NormSetTxCacheBounds(sessionHandle:long, sizeMax:long, countMin:long, countMax:long) : void - + {static} NormSetAutoParity(sessionHandle:long, autoParity:byte) : void - + {static} NormSetGrttEstimate(sessionHandle:long, grttEstimate:double) : void - + {static} NormGetGrttEstimate(sessionHandle:long) : double - + {static} NormSetGrttMax(sessionHandle:long, grttMax:double) : void - + {static} NormSetGrttProbingMode(sessionHandle:long, probingMode:NormProbingMode) : void - + {static} NormSetGrttProbingInterval(sessionHandle:long, intervalMin:double, intervalMax:double) : void - + {static} NormSetBackoffFactor(sessionHandle:long, backoffFactor:double) : void - + {static} NormSetGroupSize(sessionHandle:long, groupSize:long) : void - + {static} NormSetTxRobustFactor(sessionHandle:long, robustFactor:int) : void - + {static} NormFileEnqueue(sessionHandle:long, fileName:string, infoPtr:string, infoLen:int): long - + {static} NormDataEnqueue(sessionHandle:long, dataPtr:nint, dataLen:int, infoPtr:nint, infoLen:int) : long - + {static} NormRequeueObject(sessionHandle:long, objectHandle:long) : bool - + {static} NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:string, infoLen:int) : long - + {static} NormStreamClose(streamHandle:long, graceful:bool) : void - + {static} NormStreamWrite(streamHandle:long, buffer:byte[], numBytes:int) : int - + {static} NormStreamFlush(streamHandle:long, eom:bool, flushMode:NormFlushMode) : void - + {static} NormStreamSetAutoFlush(streamHandle:long, flushMode: NormFlushMode) : void - + {static} NormStreamSetPushEnable(streamHandle:long, pushEnable:bool) : void - + {static} NormStreamHasVacancy(streamHandle:long) : bool - + {static} NormStreamMarkEom(streamHandle:long) : void - + {static} NormSetWatermark(sessionHandle:long, objectHandle:long, overrideFlush:bool) : bool - + {static} NormResetWatermark(sessionHandle:long) : bool - + {static} NormCancelWatermark(sessionHandle:long) : void - + {static} NormAddAckingNode(sessionHandle:long, nodeId:long) : bool - + {static} NormRemoveAckingNode(sessionHandle:long, nodeId:long) : void - + {static} NormGetAckingStatus(sessionHandle:long, nodeId:long) : NormAckingStatus - + {static} NormSendCommand(sessionHandle:long, cmdBuffer:byte[], cmdLength:int, robust:bool) : bool - + {static} NormCancelCommand(sessionHandle:long) : void - + {static} NormStartReceiver(sessionHandle:long, bufferSpace:long) : bool - + {static} NormStopReceiver(sessionHandle:long) : void - + {static} NormSetRxCacheLimit(sessionHandle:long, countMax:int) : void - + {static} NormSetRxSocketBuffer(sessionHandle:long, bufferSize:long) : bool - + {static} NormSetSilentReceiver(sessionHandle:long, silent:bool, maxDelay:int) : void - + {static} NormSetDefaultUnicastNack(sessionHandle:long, unicastNacks:bool) : void - + {static} NormNodeSetUnicastNack(remoteSender:long, unicastNacks:bool) : void - + {static} NormSetDefaultSyncPolicy(sessionHandle:long, syncPolicy:NormSyncPolicy) : void - + {static} NormSetDefaultNackingMode(sessionHandle:long, nackingMode:NormNackingMode) : void - + {static} NormNodeSetNackingMode(remoteSender:long, nackingMode:NormNackingMode) : void - + {static} NormObjectSetNackingMode(objectHandle:long, nackingMode:NormNackingMode) : void - + {static} NormSetDefaultRepairBoundary(sessionHandle:long, repairBoundary:NormRepairBoundary) : void - + {static} NormNodeSetRepairBoundary(remoteSender:long, repairBoundary:NormRepairBoundary) : void - + {static} NormSetDefaultRxRobustFactor(sessionHandle:long, robustFactor:int) : void - + {static} NormNodeSetRxRobustFactor(remoteSender:long, robustFactor:int) : void - + {static} NormStreamRead(streamHandle:long, buffer:byte[], numBytes:int) : bool - + {static} NormStreamSeekMsgStart(streamHandle:long) : bool - + {static} NormStreamGetReadOffset(streamHandle:long) : long - + {static} NormObjectGetType(objectHandle:long) : NormObjectType - + {static} NormObjectHasInfo(objectHandle:long) : bool - + {static} NormObjectGetInfoLength(objectHandle:long) : int - + {static} NormObjectGetInfo(objectHandle:long, buffer:byte[], bufferLen:int) : int - + {static} NormObjectGetSize(objectHandle:long) : int - + {static} NormObjectGetBytesPending(objectHandle:long) : int - + {static} NormObjectCancel(objectHandle:long) : void - + {static} NormObjectRetain(objectHandle:long) : void - + {static} NormObjectRelease(objectHandle:long) : void - + {static} NormFileGetName(fileHandle:long, nameBuffer:string, bufferLen:int) : bool - + {static} NormFileRename(fileHandle:long, fileName:string) : bool - + {static} NormDataAccessData(objectHandle:long) : nint - + {static} NormObjectGetSender(objectHandle:long) : long - + {static} NormNodeGetId(nodeHandle:long) : long - + {static} NormNodeGetAddress(nodeHandle:long, addrBuffer:byte[], bufferLen: int, port:int) : bool - + {static} NormNodeGetGrtt(remoteSender:long) : double - + {static} NormNodeGetCommand(remoteSender:long, buffer:byte[], buflen:int) : bool - + {static} NormNodeFreeBuffers(remoteSender:long) : void - + {static} NormNodeRetain(nodeHandle:long) : void - + {static} NormNodeRelease(nodeHandle:long) : void -} -@enduml \ No newline at end of file +NormApi +-- NormEvent +NormEvent --> "Type" NormEventType +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml index e11e79e4..24152066 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormData.puml @@ -1,7 +1,7 @@ @startuml class NormData { - ~ NormData(handle:long) - + byte[] GetData() + + GetData() : byte[] + <> NormData(handle:long) } NormObject <|-- NormData -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml index 3a1a991a..6d94d58f 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormEvent.puml @@ -1,14 +1,14 @@ @startuml class NormEvent { - - _type: NormEventType - - _sessionHandle: long - - _nodeHandle: long - - _objectHandle: long + - _sessionHandle : long <> + - _nodeHandle : long <> + - _objectHandle : long <> + NormEvent(type:NormEventType, sessionHandle:long, nodeHandle:long, objectHandle:long) - + Type : NormEventType <> - + Session : NormSession <> - + Node : NormNode <> - + Object : NormObject <> + <> ToString() : string } -@enduml \ No newline at end of file +NormEvent --> "_type" NormEventType +NormEvent --> "Type" NormEventType +NormEvent --> "Session" NormSession +NormEvent --> "Node" NormNode +NormEvent --> "Object" NormObject +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml index b7c7f980..f26b63a9 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml @@ -1,9 +1,9 @@ @startuml class NormFile { + <> FILENAME_MAX : int = 260 - ~ NormFile(handle:long) + <> NormFile(handle:long) + Name : string <> - + Rename(filename:string) : void + + Rename(filePath:string) : void } NormObject <|-- NormFile -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml index 6207ae75..f9a66e74 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormInstance.puml @@ -1,24 +1,24 @@ @startuml class NormInstance { + <> NORM_DESCRIPTOR_INVALID : int = 0 - - _handle: long - + NormInstance() + - _handle : long + NormInstance(priorityBoost:bool) + + NormInstance() - CreateInstance(priorityBoost:bool) : void + DestroyInstance() : void + + CreateSession(address:string, port:int, localNodeId:long) : NormSession + + HasNextEvent(sec:int, usec:int) : bool + + HasNextEvent(waitTime:TimeSpan) : bool + + GetNextEvent(waitForEvent:bool) : NormEvent? + + GetNextEvent() : NormEvent? + + SetCacheDirectory(cachePath:string) : void + StopInstance() : void + RestartInstance() : bool + SuspendInstance() : bool + ResumeInstance() : void - + SetCacheDirectory(cachePath:string) : void - + OpenDebugLog(filename:string) : void + + OpenDebugLog(fileName:string) : void + CloseDebugLog() : void + OpenDebugPipe(pipename:string) : void - + DebugLevel: int <> <> - + HasNextEvent(sec:int, usec:int) : bool - + HasNextEvent(waitTime:TimeSpan) : bool - + GetNextEvent(waitForEvent:bool) : NormEvent - + GetNextEvent() : NormEvent - + CreateSession(address:string, port:int, localNodeId:long) : NormSession + + DebugLevel : int <> <> } -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml index 34a87df7..e583819f 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormNode.puml @@ -4,17 +4,17 @@ class NormNode { + <> NORM_NODE_NONE : int = 0 + <> NORM_NODE_INVALID : int = 0 - _handle : long - ~ NormNode(handle:long) + <> NormNode(handle:long) + + Id : long <> + + Grtt : double <> + + GetCommand(buffer:byte[], offset:int, length:int) : int + SetUnicastNack(state:bool) : void + SetNackingMode(nackingMode:NormNackingMode) : void + SetRepairBoundary(repairBoundary:NormRepairBoundary) : void + SetRxRobustFactor(robustFactor:int) : void - + Id : long <> - + Address : IPEndPoint <> - + Grtt : double <> - + Command : byte[] <> + FreeBuffers() : void + Retain() : void - + Release(): void + + Release() : void } -@enduml \ No newline at end of file +NormNode --> "Address" IPEndPoint +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml index 3c2e8c13..76960267 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormObject.puml @@ -1,18 +1,19 @@ @startuml class NormObject { + <> NORM_OBJECT_INVALID : int = 0 - # _handle:long - ~ NormObject(handle:long) - + SetNackingMode(nackingMode:NormNackingMode) : void - + Type : NormObjectType <> + # _handle : long + <> NormObject(handle:long) + + Handle : long <> + Info : byte[] <> + Size : long <> + + Sender : long <> + + SetNackingMode(nackingMode:NormNackingMode) : void + GetBytesPending() : long + Cancel() : void + Retain() : void + Release() : void - + Sender : NormNode <> + <> GetHashCode() : int - + <> Equals(obj:object) : bool + + <> Equals(obj:object?) : bool } -@enduml \ No newline at end of file +NormObject --> "Type" NormObjectType +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml index 443590c3..6a6c9ee9 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormSession.puml @@ -1,74 +1,79 @@ @startuml class NormSession { + <> NORM_SESSION_INVALID : int = 0 - {static} -_normSessions : Dictionary - _handle : long - ~ NormSession(handle:long) - {static} ~ NormSession GetSession(handle:long) + + LocalNodeId : long <> + + ReportInterval : double <> <> + + TxRate : double <> <> + + GrttEstimate : double <> <> + <> NormSession(handle:long) + <> {static} GetSession(handle:long) : NormSession? + DestroySession() : void - DestroySessionNative() : void - + LocalNodeId: long <> + SetTxPort(port:int) : void - + SetTxPort(port:int, enableReuse:bool, txAddress:string) : void + + SetTxPort(port:int, enableReuse:bool, txBindAddress:string?) : void + + SetTxOnly(txOnly:bool) : void + + SetTxOnly(txOnly:bool, connectToSessionAddress:bool) : void + SetRxPortReuse(enable:bool) : void - + SetRxPortReuse(enable:bool, rxBindAddress:string, senderAddress:string, senderPort:int) : void + + SetRxPortReuse(enable:bool, rxBindAddress:string?, senderAddress:string?, senderPort:int) : void + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool) : void + SetEcnSupport(ecnEnable:bool, ignoreLoss:bool, tolerateLoss:bool) : void + SetMulticastInterface(interfaceName:string) : void - + SetSSM(sourceAddr:string) : void + + SetSSM(sourceAddress:string) : void + SetTTL(ttl:byte) : void + SetTOS(tos:byte) : void + SetLoopback(loopbackEnable:bool) : void + SetMessageTrace(flag:bool) : void + SetTxLoss(precent:double) : void + SetRxLoss(precent:double) : void - + ReportInterval: double <> <> - + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void - + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void - + StartSender(bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void - + StartSender(bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void - + StopSender() : void - + SetTxOnly(txOnly:bool) : void - + TxRate:double <> <> - + SetFlowControl(flowControlFactor:double) : void + + SetFlowControl(precent:double) : void + SetTxSocketBuffer(bufferSize:long) : void + SetCongestionControl(enable:bool) : void + SetCongestionControl(enable:bool, adjustRate:bool) : void + SetTxRateBounds(rateMin:double, rateMax:double) : void + SetTxCacheBounds(sizeMax:long, countMin:long, countMax:long) : void + + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void + + StartSender(sessionId:int, bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void + + StartSender(bufferSpace:long, segmentSize:int, blockSize:short, numParity:short, fecId:NormFecType) : void + + StartSender(bufferSpace:long, segmentSize:int, blockSize:short, numParity:short) : void + + StopSender() : void + + FileEnqueue(filename:string) : NormFile + + FileEnqueue(filename:string, info:byte[]?, infoOffset:int, infoLength:int) : NormFile + + DataEnqueue(dataBuffer:SafeBuffer, dataOffset:int, dataLength:int) : NormData + + DataEnqueue(dataBuffer:SafeBuffer, dataOffset:int, dataLength:int, info:byte[]?, infoOffset:int, infoLength:int) : NormData + + DataEnqueue(dataPtr:nint, dataOffset:int, dataLength:int) : NormData + + DataEnqueue(dataPtr:nint, dataOffset:int, dataLength:int, info:byte[]?, infoOffset:int, infoLength:int) : NormData + + StreamOpen(bufferSize:long) : NormStream + + StreamOpen(bufferSize:long, info:byte[]?, infoOffset:int, infoLength:int) : NormStream + + StartReceiver(bufferSpace:long) : void + + StopReceiver() : void + SetAutoParity(autoParity:short) : void - + GrttEstimate:double <> <> + SetGrttMax(grttMax:double) : void + SetGrttProbingMode(probingMode:NormProbingMode) : void + SetGrttProbingInterval(intervalMin:double, intervalMax:double) : void + SetBackoffFactor(backoffFactor:double) : void + SetGroupSize(groupSize:long) : void - + SetTxRobustFactor(robustFactor:int) : void - + FileEnqueue(filename:string) : NormFile - + FileEnqueue(filename:string, info:byte[], infoOffset:int, infoLength:int) : NormFile - + DataEnqueue(dataBuffer:byte[], dataLength:int) : NormData - + DataEnqueue(dataBuffer:byte[], dataOffset:int, dataLength:int, info:byte[], infoOffset:int, infoLength:int) : NormData - + RequeueObject(object:NormObject) : void - + StreamOpen(bufferSize:long) : NormStream - + StreamOpen(bufferSize:long, info:byte[], infoOffset:int, infoLength:int) : NormStream - + SetWatermark(object:NormObject) : void - + SetWatermark(object:NormObject, overrideFlush:bool) : void + + SetTxRobustFactor(txRobustFactor:int) : void + + RequeueObject(normObject:NormObject) : void + + SetWatermark(normObject:NormObject) : void + + SetWatermark(normObject:NormObject, overrideFlush:bool) : void + CancelWatermark() : void + ResetWatermark() : void + AddAckingNode(nodeId:long) : void + RemoveAckingNode(nodeId:long) : void - + GetAckingStatus(nodeId:long): NormAckingStatus + + GetAckingStatus(nodeId:long) : NormAckingStatus + SendCommand(cmdBuffer:byte[], cmdOffset:int, cmdLength:int, robust:bool) : void + CancelCommand() : void - + StartReceiver(bufferSpace:long) : void - + StopReceiver() : void + SetRxCacheLimit(countMax:int) : void + SetRxSocketBuffer(bufferSize:long) : void + SetSilentReceiver(silent:bool, maxDelay:int) : void - + SetDefaultUnicastNack(enabled:bool) : void + + SetDefaultUnicastNack(enable:bool) : void + SetDefaultSyncPolicy(syncPolicy:NormSyncPolicy) : void - + SetDefaultNackingMode(nackingMode: NormNackingMode) : void - + SetDefaultRepairBoundary(repairBoundary: NormRepairBoundry) : void - + SetDefaultRxRobustFactor(robustFactor: int): void + + SetDefaultNackingMode(nackingMode:NormNackingMode) : void + + SetDefaultRepairBoundary(repairBoundary:NormRepairBoundary) : void + + SetDefaultRxRobustFactor(rxRobustFactor:int) : void +} +class "Dictionary`2" { } -@enduml \ No newline at end of file +NormSession o-> "_normSessions" "Dictionary`2" +@enduml diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml index bb06864b..95b215d2 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormStream.puml @@ -1,18 +1,18 @@ @startuml class NormStream { - ~ NormStream(handle:long) - + Close() : void - + Close(boolean graceful) : void - + Write(buffer:byte[], offset:int, length:int) : int - + Flush() : void - + Flush(eom:bool, flushMode:NormFlushMode) : void - + SetAutoFlush(flushMode:NormFlushMode) : void - + SetPushEnable(pushEnable:bool) : void + HasVacancy : bool <> + + ReadOffset : long <> + <> NormStream(handle:long) + + Write(buffer:byte[], offset:int, length:int) : int + MarkEom() : void + + Flush(eom:bool, flushMode:NormFlushMode) : void + + Flush() : void + + Close(graceful:bool) : void + + Close() : void + Read(buffer:byte[], offset:int, length:int) : int + SeekMsgStart() : bool - + ReadOffset : long <> + + SetPushEnable(pushEnable:bool) : void + + SetAutoFlush(flushMode:NormFlushMode) : void } NormObject <|-- NormStream -@enduml \ No newline at end of file +@enduml diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/ByteBuffer.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/ByteBuffer.cs new file mode 100644 index 00000000..d85303bd --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/ByteBuffer.cs @@ -0,0 +1,26 @@ +using System.Runtime.InteropServices; + +namespace Mil.Navy.Nrl.Norm.Buffers +{ + /// + /// A byte buffer + /// + public abstract class ByteBuffer : SafeBuffer + { + /// + /// Creates a new buffer + /// + protected ByteBuffer() : base(true) + { + } + + /// + /// Allocates a new direct byte buffer + /// + /// The new buffer's capacity, in bytes + /// The new byte buffer + public static ByteBuffer AllocateDirect(int capacity) { + return new DirectByteBuffer(capacity); + } + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/DirectByteBuffer.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/DirectByteBuffer.cs new file mode 100644 index 00000000..077a9a04 --- /dev/null +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Buffers/DirectByteBuffer.cs @@ -0,0 +1,30 @@ +using System.Runtime.InteropServices; + +namespace Mil.Navy.Nrl.Norm.Buffers +{ + /// + /// A direct byte buffer + /// + internal sealed class DirectByteBuffer : ByteBuffer + { + /// + /// Creates a new direct byte buffer with given capacity + /// + /// The new buffer's capacity, in bytes + internal DirectByteBuffer(int capacity) + { + SetHandle(Marshal.AllocHGlobal(capacity)); + Initialize(Convert.ToUInt64(capacity)); + } + + /// + /// Executes the code required to free the handle + /// + /// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false + protected override bool ReleaseHandle() + { + Marshal.FreeHGlobal(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj b/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj index efa14686..ef5a5fed 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/Mil.Navy.Nrl.Norm.csproj @@ -4,6 +4,7 @@ net6.0 enable enable + true 1.0.0 diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index e5f56b88..d1085efa 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -524,7 +524,7 @@ public struct NormEvent /// NORM_INFO content is left to the application's discretion /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. [DllImport(NORM_LIBRARY)] - public static extern long NormFileEnqueue(long sessionHandle, string fileName, byte[]? infoPtr, int infoLen); + public static extern long NormFileEnqueue(long sessionHandle, string fileName, nint infoPtr, int infoLen); /// /// This function enqueues a segment of application memory space for transmission within the specified NORM sessionHandle. @@ -571,7 +571,7 @@ public struct NormEvent /// which will enable NORM receiver applications to properly interpret the received stream as it is being received. /// A NormObjectHandle is returned which the application may use in other NORM API calls as needed. [DllImport(NORM_LIBRARY)] - public static extern long NormStreamOpen(long sessionHandle, long bufferSize, byte[]? infoPtr, int infoLen); + public static extern long NormStreamOpen(long sessionHandle, long bufferSize, nint infoPtr, int infoLen); /// /// This function halts transfer of the stream specified by the streamHandle parameter and releases any resources @@ -591,7 +591,7 @@ public struct NormEvent /// The numBytes parameter indicates the length of the data content. /// This function returns the number of bytes of data successfully enqueued for NORM stream transmission. [DllImport(NORM_LIBRARY)] - internal static extern int NormStreamWrite(long streamHandle, byte[] buffer, int numBytes); + internal static extern int NormStreamWrite(long streamHandle, nint buffer, int numBytes); /// /// This function causes an immediate "flush" of the transmit stream specified by the streamHandle parameter. @@ -725,7 +725,7 @@ public struct NormEvent /// operation (see NormStartSender()), the cmdLength exceeds the configured session segmentLength, or a previously- /// enqueued command has not yet been sent. [DllImport(NORM_LIBRARY)] - public static extern bool NormSendCommand(long sessionHandle, byte[] cmdBuffer, int cmdLength, bool robust); + public static extern bool NormSendCommand(long sessionHandle, nint cmdBuffer, int cmdLength, bool robust); /// /// This function terminates any pending NORM_CMD(APPLICATION) transmission that was previously initiated with the NormSendCommand() call. @@ -892,9 +892,10 @@ public struct NormEvent /// The buffer parameter must be a pointer to an array where the received /// data can be stored of a length as referenced by the numBytes pointer /// Specifies the length of data. - /// + /// This function normally returns a value of true. However, if a break in the integrity of the reliable received stream + /// occurs(or the stream has been ended by the sender), a value of false is returned to indicate the break. [DllImport(NORM_LIBRARY)] - public static extern bool NormStreamRead(long streamHandle, byte[] buffer, ref int numBytes); + public static extern bool NormStreamRead(long streamHandle, nint buffer, ref int numBytes); /// /// This function advances the read offset of the receive stream referenced by the streamHandle parameter to align @@ -960,7 +961,7 @@ public struct NormEvent /// function can be used to determine the length of NORM_INFO content for the object even if a NULL buffer value and /// zero bufferLen is provided. A zero value is returned if NORM_INFO content has not yet been received (or is nonexistent) for the specified object. [DllImport(NORM_LIBRARY)] - public static extern int NormObjectGetInfo(long objectHandle, [Out] byte[] buffer, int bufferLen); + public static extern int NormObjectGetInfo(long objectHandle, nint buffer, int bufferLen); /// /// This function can be used to determine the size (in bytes) of the transport object specified by the objectHandle parameter. @@ -1019,7 +1020,7 @@ public struct NormEvent /// does not refer to an object of type NORM_OBJECT_FILE. /// [DllImport(NORM_LIBRARY)] - public static extern bool NormFileGetName(long fileHandle, [Out] char[] nameBuffer, int bufferLen); + public static extern bool NormFileGetName(long fileHandle, nint nameBuffer, int bufferLen); /// /// This function renames the file used to store content for the NORM_OBJECT_FILE transport object specified by @@ -1080,7 +1081,7 @@ public struct NormEvent /// port number and/or specify a specific source address binding that is used for packet transmission. /// A value of true is returned upon success and false upon failure. An invalid nodeHandle parameter value would lead to such failure. [DllImport(NORM_LIBRARY)] - public static extern bool NormNodeGetAddress(long nodeHandle, [Out] byte[] addrBuffer, ref int bufferLen, out int port); + public static extern bool NormNodeGetAddress(long nodeHandle, nint addrBuffer, ref int bufferLen, out int port); /// /// This function retrieves the advertised estimate of group round-trip timing (GRTT) for the remote sender referenced by the given nodeHandle value. @@ -1097,13 +1098,13 @@ public struct NormEvent /// This function retrieves the content of an application-defined command that was received from a remote sender associated with the given nodeHandle. /// /// notification for a given remote sender when multiple senders may be providing content - /// Allocated system resources for each active sender + /// Allocated system resources for each active sender /// A return value of false indicates that either no command was available or the provided buffer size /// This function returns true upon successful retrieval of command content. A return value of false indicates that /// either no command was available or the provided buffer size (buflen parameter) was inadequate. /// The value referenced by the buflen parameter is adjusted to indicate the actual command length (in bytes) upon return. [DllImport(NORM_LIBRARY)] - public static extern bool NormNodeGetCommand(long remoteSender, [Out] byte[] buffer, ref int buflen); + public static extern bool NormNodeGetCommand(long remoteSender, nint cmdBuffer, ref int buflen); /// /// This function releases memory resources that were allocated for a remote sender. diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs index a627a480..0146f670 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs @@ -1,4 +1,7 @@ -namespace Mil.Navy.Nrl.Norm +using System.Runtime.InteropServices; +using System.Text; + +namespace Mil.Navy.Nrl.Norm { /// /// A transport object of type NORM_OBJECT_FILE. @@ -26,13 +29,24 @@ public string Name { get { - var buffer = new char[FILENAME_MAX]; - if (!NormFileGetName(_handle, buffer, FILENAME_MAX)) + var buffer = new byte[FILENAME_MAX]; + var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + + try + { + var bufferPtr = bufferHandle.AddrOfPinnedObject(); + if (!NormFileGetName(_handle, bufferPtr, FILENAME_MAX)) + { + throw new IOException("Failed to get file name"); + } + } + finally { - throw new IOException("Failed to get file name"); + bufferHandle.Free(); } + buffer = buffer.Where(c => c != 0).ToArray(); - return new string(buffer); + return new string(buffer.Select(Convert.ToChar).ToArray()); } } diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs index dbb9c7d2..46dba7a1 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs @@ -1,4 +1,6 @@ -using System.Net; +using Mil.Navy.Nrl.Norm.Buffers; +using System.Net; +using System.Runtime.InteropServices; namespace Mil.Navy.Nrl.Norm { @@ -45,14 +47,23 @@ public IPEndPoint Address { get { - var buffer = new byte[256]; - var bufferLength = buffer.Length; - if (!NormNodeGetAddress(_handle, buffer, ref bufferLength, out var port)) + var bufferLength = 256; + using var buffer = ByteBuffer.AllocateDirect(bufferLength); + int port; + + unsafe { - throw new IOException("Failed to get node address"); + byte* addrBuffer = null; + buffer.AcquirePointer(ref addrBuffer); + if (!NormNodeGetAddress(_handle, (nint)addrBuffer, ref bufferLength, out port)) + { + throw new IOException("Failed to get node address"); + } } - buffer = buffer.Take(bufferLength).ToArray(); - var ipAddressText = string.Join('.', buffer); + + var addressBytes = new byte[bufferLength]; + buffer.ReadArray(0, addressBytes, 0, bufferLength); + var ipAddressText = string.Join('.', addressBytes); var ipAddress = IPAddress.Parse(ipAddressText); return new IPEndPoint(ipAddress, port); @@ -65,20 +76,35 @@ public IPEndPoint Address public double Grtt => NormNodeGetGrtt(_handle); /// - /// NORM application-defined command for transmission. + /// This function retrieves the content of an application-defined command that was received from a remote sender. /// - public byte[] Command + /// Thrown when the offset or length are outside of the buffer. + public int GetCommand(byte[] buffer, int offset, int length) { - get + if (offset < 0 || offset >= buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset), "The offset is out of range"); + } + if (length < 1 || offset + length > buffer.Length) { - var buffer = new byte[256]; - var bufferLength = buffer.Length; - if (!NormNodeGetCommand(_handle, buffer, ref bufferLength)) + throw new ArgumentOutOfRangeException(nameof(length), "The length is out of range"); + } + + var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + + try + { + var bufferPtr = bufferHandle.AddrOfPinnedObject() + offset; + if (!NormNodeGetCommand(_handle, bufferPtr, ref length)) { throw new IOException("Failed to get command"); } - return buffer.Take(bufferLength).ToArray(); + } + finally + { + bufferHandle.Free(); } + return length; } /// diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs index 4d7ffefe..f3ded3ae 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs @@ -1,4 +1,6 @@ -namespace Mil.Navy.Nrl.Norm +using System.Runtime.InteropServices; + +namespace Mil.Navy.Nrl.Norm { /// /// The base transport object. @@ -39,9 +41,21 @@ public byte[]? Info { return null; } + var length = NormObjectGetInfoLength(_handle); var buffer = new byte[length]; - NormObjectGetInfo(_handle, buffer, length); + var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + + try + { + var bufferPtr = bufferHandle.AddrOfPinnedObject(); + NormObjectGetInfo(_handle, bufferPtr, length); + } + finally + { + bufferHandle.Free(); + } + return buffer; } } diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs index b7a318bf..2aa968e5 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormSession.cs @@ -466,23 +466,35 @@ public NormFile FileEnqueue(string filename) /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. /// A NormFile is returned which the application may use in other NORM API calls as needed. /// Thrown when NormFileEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue file. - public NormFile FileEnqueue(string filename, byte[] info, int infoOffset, int infoLength) + /// Thrown when the info offset or info length are outside of the info buffer. + public NormFile FileEnqueue(string filename, byte[]? info, int infoOffset, int infoLength) { - byte[]? infoBytes; - if (info != null) + if (infoOffset < 0 || infoOffset >= info?.Length) { - infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); + throw new ArgumentOutOfRangeException(nameof(infoOffset), "The info offset is out of range"); } - else + if (info != null && infoLength < 1 || infoOffset + infoLength > info?.Length) { - infoBytes = null; - infoLength = 0; + throw new ArgumentOutOfRangeException(nameof(infoLength), "The info length is out of range"); } - var objectHandle = NormFileEnqueue(_handle, filename, infoBytes, infoLength); - if (objectHandle == NormObject.NORM_OBJECT_INVALID) + + long objectHandle; + var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned); + + try + { + var infoPtr = infoHandle.AddrOfPinnedObject() + infoOffset; + objectHandle = NormFileEnqueue(_handle, filename, infoPtr, infoLength); + if (objectHandle == NormObject.NORM_OBJECT_INVALID) + { + throw new IOException("Failed to enqueue file"); + } + } + finally { - throw new IOException("Failed to enqueue file"); + infoHandle.Free(); } + return new NormFile(objectHandle); } @@ -499,7 +511,7 @@ public NormFile FileEnqueue(string filename, byte[] info, int infoOffset, int in /// A NormData is returned which the application may use in other NORM API calls as needed. /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. /// Thrown when the data offset or data length are outside of the data buffer. - public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) + public NormData DataEnqueue(SafeBuffer dataBuffer, int dataOffset, int dataLength) { return DataEnqueue(dataBuffer, dataOffset, dataLength, null, 0, 0); } @@ -517,32 +529,73 @@ public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength) /// A NormData is returned which the application may use in other NORM API calls as needed. /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. /// Thrown when the data offset, data length, info offset or info length are outside of the associated buffer. - public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength) + public NormData DataEnqueue(SafeBuffer dataBuffer, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength) { - if (dataOffset < 0 || dataOffset >= dataBuffer.Length) + if (dataOffset < 0 || Convert.ToUInt64(dataOffset) >= dataBuffer.ByteLength) { throw new ArgumentOutOfRangeException(nameof(dataOffset), "The data offset is out of range"); } - if (dataOffset + dataLength > dataBuffer.Length) + if (dataLength < 1 || Convert.ToUInt64(dataOffset + dataLength) > dataBuffer.ByteLength) { throw new ArgumentOutOfRangeException(nameof(dataLength), "The data length is out of range"); } + + unsafe + { + byte* dataPtr = null; + dataBuffer.AcquirePointer(ref dataPtr); + return DataEnqueue((nint)dataPtr, dataOffset, dataLength, info, infoOffset, infoLength); + } + } + + /// + /// This function enqueues a segment of application memory space for transmission. + /// + /// + /// This is an overload which will call DataEnqueue() with info set to null, infoOffset set to 0, and infoLength set to 0. + /// + /// The dataPtr is a pointer to the message to be transmitted. + /// Indicates the start of the message. Anything before it will not be sent. + /// Note: to send full message dataOffset should be set to 0. + /// Size of the message. + /// A NormData is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. + /// Thrown when the data offset or data length are outside of the data buffer. + public NormData DataEnqueue(nint dataPtr, int dataOffset, int dataLength) + { + return DataEnqueue(dataPtr, dataOffset, dataLength, null, 0, 0); + } + + /// + /// This function enqueues a segment of application memory space for transmission. + /// + /// The dataPtr is a pointer to the message to be transmitted. + /// Indicates the start of the message. Anything before it will not be sent. + /// Note: to send full message dataOffset should be set to 0. + /// Size of the message. + /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. + /// Indicates the start of the message. + /// The optional info and infoLength parameters are used to associate NORM_INFO content with the sent transport object. + /// A NormData is returned which the application may use in other NORM API calls as needed. + /// Thrown when NormDataEnqueue() returns NORM_OBJECT_INVALID, indicating the failure to enqueue data. + /// Thrown when the data offset, data length, info offset or info length are outside of the associated buffer. + public NormData DataEnqueue(nint dataPtr, int dataOffset, int dataLength, byte[]? info, int infoOffset, int infoLength) + { if (infoOffset < 0 || infoOffset >= info?.Length) { throw new ArgumentOutOfRangeException(nameof(infoOffset), "The info offset is out of range"); } - if (infoOffset + infoLength > info?.Length) + if (info != null && infoLength < 1 || infoOffset + infoLength > info?.Length) { throw new ArgumentOutOfRangeException(nameof(infoLength), "The info length is out of range"); } long objectHandle; - var dataHandle = GCHandle.Alloc(dataBuffer, GCHandleType.Pinned); var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned); try { - var dataPtr = dataHandle.AddrOfPinnedObject() + dataOffset; + dataPtr += dataOffset; var infoPtr = infoHandle.AddrOfPinnedObject() + infoOffset; objectHandle = NormDataEnqueue(_handle, dataPtr, dataLength, infoPtr, infoLength); if (objectHandle == NormObject.NORM_OBJECT_INVALID) @@ -552,7 +605,6 @@ public NormData DataEnqueue(byte[] dataBuffer, int dataOffset, int dataLength, b } finally { - dataHandle.Free(); infoHandle.Free(); } @@ -597,23 +649,35 @@ public NormStream StreamOpen(long bufferSize) /// Size of the message. /// A NormStream is returned which the application may use in other NORM API calls as needed. /// Thrown when NormStreamOpen() returns NORM_OBJECT_INVALID, indicating the failure to open stream. + /// Thrown when the info offset or info length are outside of the info buffer. public NormStream StreamOpen(long bufferSize, byte[]? info, int infoOffset, int infoLength) { - byte[]? infoBytes; - if (info != null) + if (infoOffset < 0 || infoOffset >= info?.Length) { - infoBytes = info.Skip(infoOffset).Take(infoLength).ToArray(); + throw new ArgumentOutOfRangeException(nameof(infoOffset), "The info offset is out of range"); } - else + if (info != null && infoLength < 1 || infoOffset + infoLength > info?.Length) { - infoBytes = null; - infoLength = 0; + throw new ArgumentOutOfRangeException(nameof(infoLength), "The info length is out of range"); } - var objectHandle = NormStreamOpen(_handle, bufferSize, infoBytes, infoLength); - if (objectHandle == NormObject.NORM_OBJECT_INVALID) + + long objectHandle; + var infoHandle = GCHandle.Alloc(info, GCHandleType.Pinned); + + try { - throw new IOException("Failed to open stream"); + var infoPtr = infoHandle.AddrOfPinnedObject() + infoOffset; + objectHandle = NormStreamOpen(_handle, bufferSize, infoPtr, infoLength); + if (objectHandle == NormObject.NORM_OBJECT_INVALID) + { + throw new IOException("Failed to open stream"); + } + } + finally + { + infoHandle.Free(); } + return new NormStream(objectHandle); } @@ -825,16 +889,37 @@ public NormAckingStatus GetAckingStatus(long nodeId) /// This function enqueues a NORM application-defined command for transmission. /// /// The cmdBuffer parameter points to a buffer containing the application-defined command content that will be contained in the NORM_CMD(APPLICA-TION) message payload. + /// /// The cmdLength indicates the length of this content (in bytes) and MUST be less than or equal to the segmentLength value for the given session. /// The command is NOT delivered reliably, /// but can be optionally transmitted with repetition (once per GRTT) according to the NORM transmit robust factor /// value for the given session if the robust parameter is set to true. /// Thrown when NormSendCommand() returns false, indicating the failure to send command. - public void SendCommand(byte[] cmdBuffer, int cmdLength, bool robust) + /// Thrown when the offset or length are outside of the buffer. + public void SendCommand(byte[] cmdBuffer, int cmdOffset, int cmdLength, bool robust) { - if(!NormSendCommand(_handle, cmdBuffer, cmdLength, robust)) + if (cmdOffset < 0 || cmdOffset >= cmdBuffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(cmdOffset), "The offset is out of range"); + } + if (cmdLength < 1 || cmdOffset + cmdLength > cmdBuffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(cmdLength), "The command length is out of range"); + } + + var commandHandle = GCHandle.Alloc(cmdBuffer, GCHandleType.Pinned); + + try + { + var cmdPtr = commandHandle.AddrOfPinnedObject() + cmdOffset; + if (!NormSendCommand(_handle, cmdPtr, cmdLength, robust)) + { + throw new IOException("Failed to send command"); + } + } + finally { - throw new IOException("Failed to send command"); + commandHandle.Free(); } } diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs index 3732051d..c6b39d18 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs @@ -1,4 +1,6 @@ -namespace Mil.Navy.Nrl.Norm +using System.Runtime.InteropServices; + +namespace Mil.Navy.Nrl.Norm { /// /// A transport object of type NORM_OBJECT_STREAM. @@ -44,10 +46,32 @@ internal NormStream(long handle) : base(handle) /// Note: If the data is written in its entirety, offset should be set to 0. /// The length parameter indicates the length of the data content. /// This function returns the number of bytes of data successfully enqueued for NORM stream transmission. + /// Thrown when the offset or length are outside of the buffer. public int Write(byte[] buffer, int offset, int length) { - var bytes = buffer.Skip(offset).Take(length).ToArray(); - return NormStreamWrite(_handle, bytes, length); + if (offset < 0 || offset >= buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset), "The offset is out of range"); + } + if (length < 1 || offset + length > buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(length), "The length is out of range"); + } + + int numBytes; + var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + + try + { + var bufferPtr = bufferHandle.AddrOfPinnedObject() + offset; + numBytes = NormStreamWrite(_handle, bufferPtr, length); + } + finally + { + bufferHandle.Free(); + } + + return numBytes; } /// @@ -122,22 +146,31 @@ public void Close() /// Note: To read the data in its entirety, begin at offset 0. /// Expected length of data received /// The length of data received + /// Thrown when the offset or length are outside of the buffer. public int Read(byte[] buffer, int offset, int length) { - var bytes = new byte[length]; - - if (!NormStreamRead(_handle, bytes, ref length)) + if (offset < 0 || offset >= buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset), "The offset is out of range"); + } + if (length < 1 || offset + length > buffer.Length) { - return -1; + throw new ArgumentOutOfRangeException(nameof(length), "The length is out of range"); } - for (var i = 0; i < length; i++) + var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + + try { - var bufferPosition = offset + i; - if (bufferPosition < buffer.Length) + var bufferPtr = bufferHandle.AddrOfPinnedObject() + offset; + if (!NormStreamRead(_handle, bufferPtr, ref length)) { - buffer[bufferPosition] = bytes[i]; - } + length = -1; + } + } + finally + { + bufferHandle.Free(); } return length; diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs index e8111326..60bd071d 100644 --- a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormInstanceTests.cs @@ -1,4 +1,5 @@ using Bogus; +using Mil.Navy.Nrl.Norm.Buffers; using System.Text; namespace Mil.Navy.Nrl.Norm.IntegrationTests @@ -216,7 +217,7 @@ public void SetsDebugLevel() Assert.Equal(expectedDebugLevel, actualDebugLevel); } - [Fact] + [SkippableFact(typeof(IOException))] public void HasEventsFromTimeSpan() { var faker = new Faker(); @@ -230,14 +231,16 @@ public void HasEventsFromTimeSpan() var dataContent = faker.Lorem.Paragraph(); var data = Encoding.ASCII.GetBytes(dataContent); - _normSession.DataEnqueue(data, 0, data.Length); + using var dataBuffer = ByteBuffer.AllocateDirect(data.Length); + dataBuffer.WriteArray(0, data, 0, data.Length); + _normSession.DataEnqueue(dataBuffer, 0, data.Length); Assert.True(_normInstance.HasNextEvent(TimeSpan.FromSeconds(1.5))); _normSession.StopSender(); } - [Fact] + [SkippableFact(typeof(IOException))] public void HasEventsFromSecondsAndMicroseconds() { var faker = new Faker(); @@ -251,7 +254,9 @@ public void HasEventsFromSecondsAndMicroseconds() var dataContent = faker.Lorem.Paragraph(); var data = Encoding.ASCII.GetBytes(dataContent); - _normSession.DataEnqueue(data, 0, data.Length); + using var dataBuffer = ByteBuffer.AllocateDirect(data.Length); + dataBuffer.WriteArray(0, data, 0, data.Length); + _normSession.DataEnqueue(dataBuffer, 0, data.Length); Assert.True(_normInstance.HasNextEvent(1, 500000)); diff --git a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs index a478299f..39d24d5c 100644 --- a/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs +++ b/src/dotnet/tests/Mil.Navy.Nrl.Norm.IntegrationTests/NormSessionTests.cs @@ -1,5 +1,7 @@ using Bogus; +using Mil.Navy.Nrl.Norm.Buffers; using Mil.Navy.Nrl.Norm.Enums; +using System.Net; using System.Text; namespace Mil.Navy.Nrl.Norm.IntegrationTests @@ -240,8 +242,12 @@ private void AssertNormEvents(IEnumerable normEvents) { var actualId = normNode.Id; Assert.NotEqual(NormNode.NORM_NODE_NONE, actualId); + var expectedIpAddresses = Dns.GetHostAddresses(Dns.GetHostName()) + .Where(i => i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !IPAddress.IsLoopback(i)); var actualAddress = normNode.Address; Assert.NotNull(actualAddress); + Assert.Contains(actualAddress.Address, expectedIpAddresses); + Assert.NotEqual(default, actualAddress.Port); var actualGrtt = normNode.Grtt; Assert.NotEqual(-1, actualGrtt); var expectedEventString = $"NormEvent [type={normEvent.Type}]"; @@ -251,8 +257,41 @@ private void AssertNormEvents(IEnumerable normEvents) } } - [SkippableFact(typeof(IOException))] - public void EnqueuesFile() + public static IEnumerable GenerateInfo() + { + var info = new List(); + + var infoContent = GenerateInfoContent(); + var expectedInfoContent = infoContent; + var infoOffset = 0; + var infoLength = infoContent.Length; + info.Add(new object?[] { infoContent, expectedInfoContent, infoOffset, infoLength }); + + var faker = new Faker(); + infoLength = faker.Random.Int(infoContent.Length / 2, infoContent.Length - 1); + expectedInfoContent = infoContent.Substring(infoOffset, infoLength); + info.Add(new object?[] { infoContent, expectedInfoContent, infoOffset, infoLength }); + + infoOffset = faker.Random.Int(1, (infoContent.Length - 1) / 2); + infoLength = infoContent.Length - infoOffset; + expectedInfoContent = infoContent.Substring(infoOffset, infoLength); + info.Add(new object?[] { infoContent, expectedInfoContent, infoOffset, infoLength }); + + infoOffset = faker.Random.Int(1, (infoContent.Length - 1) / 2); + infoLength = faker.Random.Int(1, infoContent.Length - infoOffset); + expectedInfoContent = infoContent.Substring(infoOffset, infoLength); + info.Add(new object?[] { infoContent, expectedInfoContent, infoOffset, infoLength }); + + info.Add(new object?[] { null, null, null, null }); + + info.Add(new object?[] { null, "", 0, 0 }); + + return info; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateInfo))] + public void EnqueuesFile(string? infoContent = null, string? expectedInfoContent = null, int? infoOffset = null, int? infoLength = null) { StartSender(); @@ -260,10 +299,27 @@ public void EnqueuesFile() var fileName = Guid.NewGuid().ToString(); var filePath = Path.Combine(_testPath, fileName); File.WriteAllText(filePath, fileContent); + //Create info to enqueue + byte[]? info = null; + if (infoContent != null) { + info = Encoding.ASCII.GetBytes(infoContent); + } + else if (expectedInfoContent == null) + { + expectedInfoContent = filePath; + } + + var expectedInfo = Array.Empty(); + if (expectedInfoContent != null) + { + expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent); + } try { - var normFile = _normSession.FileEnqueue(filePath); + var normFile = infoOffset != null && infoLength != null ? + _normSession.FileEnqueue(filePath, info, infoOffset.Value, infoLength.Value) : + _normSession.FileEnqueue(filePath); Assert.NotNull(normFile); var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; var actualEvents = GetEvents(); @@ -272,6 +328,13 @@ public void EnqueuesFile() var expectedFileName = filePath; var actualFileName = normFile.Name; Assert.Equal(expectedFileName, actualFileName); + var actualInfo = normFile.Info; + Assert.Equal(expectedInfo, actualInfo); + if (actualInfo != null) + { + var actualInfoContent = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedInfoContent, actualInfoContent); + } } catch (Exception) { @@ -283,8 +346,70 @@ public void EnqueuesFile() } } - [SkippableFact(typeof(IOException))] - public void ReceivesFile() + public static IEnumerable GenerateOutOfRangeInfo() + { + var info = new List(); + var faker = new Faker(); + + var infoContent = GenerateInfoContent(); + var infoOffset = faker.Random.Int(-infoContent.Length, -1); + var infoLength = infoContent.Length; + info.Add(new object[] { infoContent, infoOffset, infoLength }); + + infoOffset = faker.Random.Int(infoContent.Length, infoContent.Length * 2); + infoLength = infoContent.Length; + info.Add(new object[] { infoContent, infoOffset, infoLength }); + + infoOffset = 0; + infoLength = faker.Random.Int(infoContent.Length + 1, infoContent.Length * 2); + info.Add(new object[] { infoContent, infoOffset, infoLength }); + + infoLength = -1; + info.Add(new object[] { infoContent, infoOffset, infoLength }); + + infoLength = 0; + info.Add(new object[] { infoContent, infoOffset, infoLength }); + + infoOffset = infoContent.Length - 1; + infoLength = infoContent.Length; + info.Add(new object[] { infoContent, infoOffset, infoLength }); + + return info; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateOutOfRangeInfo))] + public void EnqueuesFileThrowsExceptionWhenOutOfRange(string? infoContent = null, int? infoOffset = null, int? infoLength = null) + { + StartSender(); + + var fileContent = GenerateTextContent(); + var fileName = Guid.NewGuid().ToString(); + var filePath = Path.Combine(_testPath, fileName); + File.WriteAllText(filePath, fileContent); + //Create info to enqueue + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + + try + { + Assert.Throws(() => + infoOffset != null && infoLength != null ? + _normSession.FileEnqueue(filePath, info, infoOffset.Value, infoLength.Value) : + _normSession.FileEnqueue(filePath)); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateInfo))] + public void ReceivesFile(string? infoContent = null, string? expectedInfoContent = null, int? infoOffset = null, int? infoLength = null) { _normSession.SetLoopback(true); StartSender(); @@ -301,11 +426,27 @@ public void ReceivesFile() var fileContent = GenerateTextContent(); var filePath = Path.Combine(_testPath, fileName); File.WriteAllText(filePath, fileContent); + //Create info to enqueue + byte[]? info = null; + if (infoContent != null) { + info = Encoding.ASCII.GetBytes(infoContent); + } + else if (expectedInfoContent == null) + { + expectedInfoContent = filePath; + } + var expectedInfo = Array.Empty(); + if (expectedInfoContent != null) + { + expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent); + } try { //Enqueue file - var normFile = _normSession.FileEnqueue(filePath); + var normFile = infoOffset != null && infoLength != null ? + _normSession.FileEnqueue(filePath, info, infoOffset.Value, infoLength.Value) : + _normSession.FileEnqueue(filePath); //Wait for events var normEvents = GetEvents(); AssertNormEvents(normEvents); @@ -329,6 +470,13 @@ public void ReceivesFile() var actualContent = File.ReadAllText(actualFileName); Assert.Equal(fileContent, actualContent); + var actualInfo = receivedNormFile.Info; + Assert.Equal(expectedInfo, actualInfo); + if (actualInfo != null) + { + var actualInfoContent = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedInfoContent, actualInfoContent); + } } catch (Exception) { @@ -429,12 +577,12 @@ public static IEnumerable GenerateData() expectedInfoContent = infoContent.Substring(infoOffset, infoLength); data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); - infoOffset = faker.Random.Int(1, infoContent.Length - 1 / 2); + infoOffset = faker.Random.Int(1, (infoContent.Length - 1) / 2); infoLength = infoContent.Length - infoOffset; expectedInfoContent = infoContent.Substring(infoOffset, infoLength); data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); - infoOffset = faker.Random.Int(1, infoContent.Length - 1 / 2); + infoOffset = faker.Random.Int(1, (infoContent.Length - 1) / 2); infoLength = faker.Random.Int(1, infoContent.Length - infoOffset); expectedInfoContent = infoContent.Substring(infoOffset, infoLength); data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength, infoContent, expectedInfoContent, infoOffset, infoLength }); @@ -443,12 +591,12 @@ public static IEnumerable GenerateData() expectedDataContent = dataContent.Substring(dataOffset, dataLength); data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); - dataOffset = faker.Random.Int(1, dataContent.Length - 1 / 2); + dataOffset = faker.Random.Int(1, (dataContent.Length - 1) / 2); dataLength = dataContent.Length - dataOffset; expectedDataContent = dataContent.Substring(dataOffset, dataLength); data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); - dataOffset = faker.Random.Int(1, dataContent.Length - 1 / 2); + dataOffset = faker.Random.Int(1, (dataContent.Length - 1) / 2); dataLength = faker.Random.Int(1, dataContent.Length - dataOffset); expectedDataContent = dataContent.Substring(dataOffset, dataLength); data.Add(new object[] { dataContent, expectedDataContent, dataOffset, dataLength }); @@ -463,6 +611,8 @@ public void EnqueuesData(string dataContent, string expectedDataContent, int dat StartSender(); //Create data to write to enqueue var data = Encoding.ASCII.GetBytes(dataContent); + using var dataBuffer = ByteBuffer.AllocateDirect(data.Length); + dataBuffer.WriteArray(0, data, 0, data.Length); var expectedData = Encoding.ASCII.GetBytes(expectedDataContent); //Create info to enqueue var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; @@ -471,8 +621,8 @@ public void EnqueuesData(string dataContent, string expectedDataContent, int dat try { var normData = infoOffset != null && infoLength != null ? - _normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : - _normSession.DataEnqueue(data, dataOffset, dataLength); + _normSession.DataEnqueue(dataBuffer, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : + _normSession.DataEnqueue(dataBuffer, dataOffset, dataLength); var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); Assert.Equal(expectedEventTypes, actualEventTypes); @@ -516,6 +666,12 @@ public static IEnumerable GenerateOutOfRangeData() dataLength = faker.Random.Int(dataContent.Length + 1, dataContent.Length * 2); data.Add(new object[] { dataContent, dataOffset, dataLength }); + dataLength = -1; + data.Add(new object[] { dataContent, dataOffset, dataLength }); + + dataLength = 0; + data.Add(new object[] { dataContent, dataOffset, dataLength }); + dataOffset = dataContent.Length - 1; dataLength = dataContent.Length; data.Add(new object[] { dataContent, dataOffset, dataLength }); @@ -536,6 +692,12 @@ public static IEnumerable GenerateOutOfRangeData() infoLength = faker.Random.Int(infoContent.Length + 1, infoContent.Length * 2); data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + infoLength = -1; + data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + + infoLength = 0; + data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); + infoOffset = infoContent.Length - 1; infoLength = infoContent.Length; data.Add(new object[] { dataContent, dataOffset, dataLength, infoContent, infoOffset, infoLength }); @@ -550,6 +712,8 @@ public void EnqueuesDataThrowsExceptionWhenOutOfRange(string dataContent, int da StartSender(); //Create data to enqueue var data = Encoding.ASCII.GetBytes(dataContent); + using var dataBuffer = ByteBuffer.AllocateDirect(data.Length); + dataBuffer.WriteArray(0, data, 0, data.Length); //Create info to enqueue var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; @@ -557,8 +721,8 @@ public void EnqueuesDataThrowsExceptionWhenOutOfRange(string dataContent, int da { Assert.Throws(() => infoOffset != null && infoLength != null ? - _normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : - _normSession.DataEnqueue(data, dataOffset, dataLength)); + _normSession.DataEnqueue(dataBuffer, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : + _normSession.DataEnqueue(dataBuffer, dataOffset, dataLength)); } catch (Exception) { @@ -586,6 +750,8 @@ public void ReceivesData(string content, string expectedDataContent, int dataOff //Create data to be sent var data = Encoding.ASCII.GetBytes(content); + using var dataBuffer = ByteBuffer.AllocateDirect(data.Length); + dataBuffer.WriteArray(0, data, 0, data.Length); var expectedData = Encoding.ASCII.GetBytes(expectedDataContent); //Create info to be sent var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; @@ -594,8 +760,8 @@ public void ReceivesData(string content, string expectedDataContent, int dataOff try { var normData = infoOffset != null && infoLength != null ? - _normSession.DataEnqueue(data, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : - _normSession.DataEnqueue(data, dataOffset, dataLength); + _normSession.DataEnqueue(dataBuffer, dataOffset, dataLength, info, infoOffset.Value, infoLength.Value) : + _normSession.DataEnqueue(dataBuffer, dataOffset, dataLength); var expectedEventTypes = new List { NormEventType.NORM_REMOTE_SENDER_NEW, @@ -619,7 +785,7 @@ public void ReceivesData(string content, string expectedDataContent, int dataOff Assert.Equal(expectedData, actualData); var actualDataContent = Encoding.ASCII.GetString(actualData); Assert.Equal(expectedDataContent, actualDataContent); - var actualInfo = normData.Info; + var actualInfo = actualNormData.Info; Assert.Equal(expectedInfo, actualInfo); if (actualInfo != null) { @@ -638,29 +804,41 @@ public void ReceivesData(string content, string expectedDataContent, int dataOff } } - [SkippableFact(typeof(IOException))] - public void SendsStream() + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateData))] + public void SendsStream(string content, string expectedContent, int offset, int length, string? infoContent = null, string expectedInfoContent = "", int? infoOffset = null, int? infoLength = null) { StartSender(); - var fileContent = GenerateTextContent(); - var data = Encoding.ASCII.GetBytes(fileContent); - var dataOffset = 0; + var buffer = Encoding.ASCII.GetBytes(content); + var expectedBuffer = Encoding.ASCII.GetBytes(expectedContent); + //Create info to enqueue + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + var expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent); NormStream? normStream = null; try { var repairWindowSize = 1024 * 1024; - normStream = _normSession.StreamOpen(repairWindowSize); + normStream = infoOffset != null && infoLength != null ? + _normSession.StreamOpen(repairWindowSize, info, infoOffset.Value, infoLength.Value) : + _normSession.StreamOpen(repairWindowSize); - var expectedBytesWritten = data.Length; - var actualBytesWritten = normStream.Write(data, dataOffset, data.Length); + var expectedBytesWritten = expectedBuffer.Length; + var actualBytesWritten = normStream.Write(buffer, offset, length); WaitForEvents(); normStream.MarkEom(); normStream.Flush(); Assert.Equal(expectedBytesWritten, actualBytesWritten); + var actualInfo = normStream.Info; + Assert.Equal(expectedInfo, actualInfo); + if (actualInfo != null) + { + var actualInfoContent = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedInfoContent, actualInfoContent); + } } catch (Exception) { @@ -673,31 +851,65 @@ public void SendsStream() } } - [SkippableFact(typeof(IOException))] - public void ReceivesStream() + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateOutOfRangeData))] + public void SendsStreamThrowsExceptionWhenOutOfRange(string content, int offset, int length, string? infoContent = null, int? infoOffset = null, int? infoLength = null) + { + StartSender(); + var buffer = Encoding.ASCII.GetBytes(content); + //Create info to enqueue + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + + if (infoOffset != null && infoLength != null) { + Assert.Throws(() => + _normSession.StreamOpen(repairWindowSize, info, infoOffset.Value, infoLength.Value)); + } + else + { + normStream = _normSession.StreamOpen(repairWindowSize); + Assert.Throws(() => + normStream.Write(buffer, offset, length)); + } + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateData))] + public void ReceivesStream(string content, string expectedContent, int offset, int length, string? infoContent = null, string expectedInfoContent = "", int? infoOffset = null, int? infoLength = null) { _normSession.SetLoopback(true); StartSender(); StartReceiver(); - //Set up cache directory - var folderName = Guid.NewGuid().ToString(); - var cachePath = Path.Combine(_testPath, folderName); - Directory.CreateDirectory(cachePath); - _normInstance.SetCacheDirectory(cachePath); - - var fileContent = GenerateTextContent(); - var data = Encoding.ASCII.GetBytes(fileContent); - var dataOffset = 0; + var buffer = Encoding.ASCII.GetBytes(content); + var expectedBuffer = Encoding.ASCII.GetBytes(expectedContent); + //Create info to enqueue + var info = infoContent != null ? Encoding.ASCII.GetBytes(infoContent) : null; + var expectedInfo = Encoding.ASCII.GetBytes(expectedInfoContent); NormStream? normStream = null; try { var repairWindowSize = 1024 * 1024; - normStream = _normSession.StreamOpen(repairWindowSize); + normStream = infoOffset != null && infoLength != null ? + _normSession.StreamOpen(repairWindowSize, info, infoOffset.Value, infoLength.Value) : + _normSession.StreamOpen(repairWindowSize); - var expectedBytesWritten = data.Length; - normStream.Write(data, dataOffset, data.Length-dataOffset); + var expectedBytesWritten = expectedBuffer.Length; + normStream.Write(buffer, offset, length); normStream.MarkEom(); normStream.Flush(); @@ -711,15 +923,22 @@ public void ReceivesStream() var receivedNormStream = Assert.IsType(normObjectEvent.Object); var numRead = 0; var receiveBuffer = new byte[65536]; - while ((numRead = receivedNormStream.Read(receiveBuffer, dataOffset, receiveBuffer.Length-dataOffset)) > 0) + while ((numRead = receivedNormStream.Read(receiveBuffer, 0, length)) > 0) { if (numRead != -1) { var receivedData = receiveBuffer.Take(numRead).ToArray(); var receivedContent = Encoding.ASCII.GetString(receivedData); - Assert.Equal(fileContent, receivedContent); + Assert.Equal(expectedContent, receivedContent); } } + var actualInfo = receivedNormStream.Info; + Assert.Equal(expectedInfo, actualInfo); + if (actualInfo != null) + { + var actualInfoContent = Encoding.ASCII.GetString(actualInfo); + Assert.Equal(expectedInfoContent, actualInfoContent); + } } catch (Exception) { @@ -800,6 +1019,90 @@ public void ReceivesStreamWithOffset() } } + public static IEnumerable GenerateOutOfRangeReceiveStream() + { + var data = new List(); + + var faker = new Faker(); + var initialLength = faker.Random.Int(256, 1024); + var dataOffset = faker.Random.Int(-initialLength, -1); + var dataLength = initialLength; + data.Add(new object[] { initialLength, dataOffset, dataLength }); + + dataOffset = faker.Random.Int(initialLength, initialLength * 2); + dataLength = initialLength; + data.Add(new object[] { initialLength, dataOffset, dataLength }); + + dataOffset = 0; + dataLength = faker.Random.Int(initialLength + 1, initialLength * 2); + data.Add(new object[] { initialLength, dataOffset, dataLength }); + + dataLength = -1; + data.Add(new object[] { initialLength, dataOffset, dataLength }); + + dataLength = 0; + data.Add(new object[] { initialLength, dataOffset, dataLength }); + + dataOffset = initialLength - 1; + dataLength = initialLength; + data.Add(new object[] { initialLength, dataOffset, dataLength }); + + return data; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateOutOfRangeReceiveStream))] + public void ReceivesStreamThrowsExceptionWhenOutOfRange(int initialReceiveBufferLength, int receiveBufferOffset, int receiveBufferLength) + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + + //Set up cache directory + var folderName = Guid.NewGuid().ToString(); + var cachePath = Path.Combine(_testPath, folderName); + Directory.CreateDirectory(cachePath); + _normInstance.SetCacheDirectory(cachePath); + + var fileContent = GenerateTextContent(); + var data = Encoding.ASCII.GetBytes(fileContent); + NormStream? normStream = null; + + try + { + var repairWindowSize = 1024 * 1024; + + normStream = _normSession.StreamOpen(repairWindowSize); + var offset = 0; + var length = data.Length; + normStream.Write(data, offset, length); + normStream.MarkEom(); + normStream.Flush(); + + var normEvents = GetEvents(); + AssertNormEvents(normEvents); + + var expectedNormEventType = NormEventType.NORM_RX_OBJECT_UPDATED; + var normObjectEvent = normEvents.First(e => e.Type == expectedNormEventType); + var receivedNormStream = Assert.IsType(normObjectEvent.Object); + + var receiveBuffer = new byte[initialReceiveBufferLength]; + + Assert.Throws(() => + receivedNormStream.Read(receiveBuffer, receiveBufferOffset, receiveBufferLength)); + } + catch (Exception) + { + throw; + } + finally + { + normStream?.Close(true); + StopSender(); + StopReceiver(); + } + } + [Fact] public void SetsTxPort() { @@ -1040,10 +1343,12 @@ public void RequeuesObject() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); var expectedEventTypes = new List { NormEventType.NORM_TX_OBJECT_SENT, NormEventType.NORM_TX_QUEUE_EMPTY }; var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); Assert.Equal(expectedEventTypes, actualEventTypes); @@ -1078,10 +1383,12 @@ public void SetsWatermark() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); _normSession.SetWatermark(normData); var expectedEventTypes = new List { @@ -1126,10 +1433,12 @@ public void CancelsWatermark() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); _normSession.SetWatermark(normData); _normSession.CancelWatermark(); var expectedEventTypes = new List @@ -1173,10 +1482,12 @@ public void ResetsWatermark() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); _normSession.SetWatermark(normData); var expectedEventTypes = new List { @@ -1243,10 +1554,12 @@ public void GetsAckingStatus() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); _normSession.SetWatermark(normData); WaitForEvents(TimeSpan.FromSeconds(1)); var expectedAckingStatus = NormAckingStatus.NORM_ACK_SUCCESS; @@ -1268,13 +1581,13 @@ public void GetsAckingStatus() public void SendsCommand() { StartSender(); - //Create data to write to the stream + //Create command to send var expectedContent = GenerateTextContent(); byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var expectedEventTypes = new List { NormEventType.NORM_TX_CMD_SENT }; var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); Assert.Equal(expectedEventTypes, actualEventTypes); @@ -1289,19 +1602,77 @@ public void SendsCommand() } } + public static IEnumerable GenerateOutOfRangeCommand() + { + var command = new List(); + var faker = new Faker(); + + var content = GenerateInfoContent(); + var offset = faker.Random.Int(-content.Length, -1); + var length = content.Length; + command.Add(new object[] { content, offset, length }); + + offset = faker.Random.Int(content.Length, content.Length * 2); + length = content.Length; + command.Add(new object[] { content, offset, length }); + + offset = 0; + length = faker.Random.Int(content.Length + 1, content.Length * 2); + command.Add(new object[] { content, offset, length }); + + length = -1; + command.Add(new object[] { content, offset, length }); + + length = 0; + command.Add(new object[] { content, offset, length }); + + offset = content.Length - 1; + length = content.Length; + command.Add(new object[] { content, offset, length }); + + return command; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateOutOfRangeCommand))] + public void SendsCommandThrowsExceptionWhenOutOfRange(string content, int offset, int length) + { + StartSender(); + //Create data to enqueue + var command = Encoding.ASCII.GetBytes(content); + + try + { + Assert.Throws(() => + _normSession.SendCommand(command, offset, length, false)); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + } + } + [SkippableFact(typeof(IOException))] public void ReceivesCommand() { _normSession.SetLoopback(true); StartSender(); StartReceiver(); - //Create data to write to the stream - var expectedContent = GenerateTextContent(); - byte[] expectedCommand = Encoding.ASCII.GetBytes(expectedContent); + //Create command to send + var content = GenerateInfoContent(); + var expectedContent = content; + var offset = 0; + var length = content.Length; + var command = Encoding.ASCII.GetBytes(content); + var expectedCommand = Encoding.ASCII.GetBytes(expectedContent); try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(command, 0, command.Length, false); var expectedEventTypes = new List { NormEventType.NORM_TX_CMD_SENT, @@ -1309,8 +1680,55 @@ public void ReceivesCommand() NormEventType.NORM_REMOTE_SENDER_ACTIVE, NormEventType.NORM_RX_CMD_NEW }; - var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); + var actualEvents = GetEvents(); + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); + Assert.Equivalent(expectedEventTypes, actualEventTypes); + + var actualEvent = actualEvents.First(e => e.Type == NormEventType.NORM_RX_CMD_NEW); + var actualCommand = new byte[command.Length]; + var actualLength = actualEvent?.Node?.GetCommand(actualCommand, offset, length); + Assert.Equal(length, actualLength); + Assert.Equal(expectedCommand, actualCommand); + var actualContent = Encoding.ASCII.GetString(actualCommand); + Assert.Equal(expectedContent, actualContent); + } + catch (Exception) + { + throw; + } + finally + { + StopSender(); + StopReceiver(); + } + } + + private void ReceivesCommandThrowsException(string content, int offset, int length) where TExceptionType : Exception + { + _normSession.SetLoopback(true); + StartSender(); + StartReceiver(); + //Create command to send + var command = Encoding.ASCII.GetBytes(content); + + try + { + _normSession.SendCommand(command, 0, command.Length, false); + var expectedEventTypes = new List + { + NormEventType.NORM_TX_CMD_SENT, + NormEventType.NORM_REMOTE_SENDER_NEW, + NormEventType.NORM_REMOTE_SENDER_ACTIVE, + NormEventType.NORM_RX_CMD_NEW + }; + var actualEvents = GetEvents(); + var actualEventTypes = actualEvents.Select(e => e.Type).ToList(); Assert.Equivalent(expectedEventTypes, actualEventTypes); + + var actualEvent = actualEvents.First(e => e.Type == NormEventType.NORM_RX_CMD_NEW); + var actualCommand = new byte[command.Length]; + Assert.Throws(() => + actualEvent?.Node?.GetCommand(actualCommand, offset, length)); } catch (Exception) { @@ -1323,6 +1741,72 @@ public void ReceivesCommand() } } + public static IEnumerable GenerateShortLengthCommand() + { + var command = new List(); + + var content = GenerateInfoContent(); + var faker = new Faker(); + var offset = 0; + var length = faker.Random.Int(content.Length / 2, content.Length - 1); + command.Add(new object[] { content, offset, length }); + + offset = faker.Random.Int(1, (content.Length - 1) / 2); + length = content.Length - offset; + command.Add(new object[] { content, offset, length }); + + offset = faker.Random.Int(1, (content.Length - 1) / 2); + length = faker.Random.Int(1, content.Length - offset); + command.Add(new object[] { content, offset, length }); + + return command; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateShortLengthCommand))] + public void ReceivesCommandThrowsExceptionWhenLengthLessThanCommand(string content, int offset, int length) + { + ReceivesCommandThrowsException(content, offset, length); + } + + public static IEnumerable GenerateOutOfRangeReceiveCommand() + { + var command = new List(); + + var content = GenerateTextContent(); + var faker = new Faker(); + var offset = faker.Random.Int(-content.Length, -1); + var length = content.Length; + command.Add(new object[] { content, offset, length }); + + offset = faker.Random.Int(content.Length, content.Length * 2); + length = content.Length; + command.Add(new object[] { content, offset, length }); + + offset = 0; + length = faker.Random.Int(content.Length + 1, content.Length * 2); + command.Add(new object[] { content, offset, length }); + + length = -1; + command.Add(new object[] { content, offset, length }); + + length = 0; + command.Add(new object[] { content, offset, length }); + + offset = content.Length - 1; + length = content.Length; + command.Add(new object[] { content, offset, length }); + + return command; + } + + [SkippableTheory(typeof(IOException))] + [MemberData(nameof(GenerateOutOfRangeReceiveCommand))] + public void ReceivesCommandThrowsExceptionWhenOutOfRange(string content, int offset, int length) + { + ReceivesCommandThrowsException(content, offset, length); + } + [SkippableFact(typeof(IOException))] public void CancelsCommand() { @@ -1333,7 +1817,7 @@ public void CancelsCommand() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); _normSession.CancelCommand(); var expectedEventTypes = new List(); var actualEventTypes = GetEvents().Select(e => e.Type).ToList(); @@ -1390,7 +1874,7 @@ public void SetsUnicastNack() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1464,7 +1948,7 @@ public void SetsNackingMode() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1510,7 +1994,7 @@ public void SetsRepairBoundary() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1549,7 +2033,7 @@ public void SetsRxRobustFactor() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1581,7 +2065,7 @@ public void GetsCommand() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1589,7 +2073,9 @@ public void GetsCommand() var actualNode = actualEvent.Node; Assert.NotNull(actualNode); - var actualCommand = actualNode.Command; + var actualCommand = new byte[expectedCommand.Length]; + var actualLength = actualEvent?.Node?.GetCommand(actualCommand, 0, expectedCommand.Length); + Assert.Equal(expectedCommand.Length, actualLength); Assert.Equal(expectedCommand, actualCommand); var actualContent = Encoding.ASCII.GetString(actualCommand); Assert.Equal(expectedContent, actualContent); @@ -1617,7 +2103,7 @@ public void FreesBuffers() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1649,7 +2135,7 @@ public void RetainsAndReleases() try { - _normSession.SendCommand(expectedCommand, expectedCommand.Length, false); + _normSession.SendCommand(expectedCommand, 0, expectedCommand.Length, false); var normEventType = NormEventType.NORM_RX_CMD_NEW; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -1677,10 +2163,12 @@ public void GetsObjectType_DATA() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); Assert.Equal(NormObjectType.NORM_OBJECT_DATA, normData.Type); } catch (Exception) @@ -1752,10 +2240,12 @@ public void GetsObjectSize() var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); var expectedSize = Encoding.ASCII.GetByteCount(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); Assert.Equal(expectedSize, normData.Size); } catch (Exception) @@ -1775,10 +2265,12 @@ public void SetsObjectNackingMode_NORM_NACK_NONE() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); normData.SetNackingMode(NormNackingMode.NORM_NACK_NONE); } catch (Exception) @@ -1798,10 +2290,12 @@ public void SetsObjectNackingMode_NORM_NACK_INFO_ONLY() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); normData.SetNackingMode(NormNackingMode.NORM_NACK_INFO_ONLY); } catch (Exception) @@ -1821,10 +2315,12 @@ public void SetsObjectNackingMode_NORM_NACK_NORMAL() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); normData.SetNackingMode(NormNackingMode.NORM_NACK_NORMAL); } catch (Exception) @@ -1845,10 +2341,12 @@ public void GetsBytesPending() var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); var expectedBytesPending = (long)0; + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); WaitForEvents(); Assert.Equal(expectedBytesPending, normData.GetBytesPending()); } @@ -1869,10 +2367,12 @@ public void CancelsObject() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); normData.Cancel(); } catch (Exception) @@ -1892,10 +2392,12 @@ public void RetainsObject() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); normData.Retain(); } catch (Exception) @@ -1915,10 +2417,12 @@ public void ReleasesObject() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); normData.Retain(); normData.Release(); } @@ -1939,10 +2443,12 @@ public void GetsSenderThrowsException() //Create data to write to the stream var expectedContent = GenerateTextContent(); byte[] expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); Assert.Throws(() => normData.Sender); } catch (Exception) @@ -1971,10 +2477,12 @@ public void GetsSender() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); var normEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -2009,10 +2517,12 @@ public void GetsObjectHashCode() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - var normData = _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + var normData = _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); var normEventType = NormEventType.NORM_RX_OBJECT_COMPLETED; var actualEvents = GetEvents(); Assert.Contains(normEventType, actualEvents.Select(e => e.Type)); @@ -2049,10 +2559,12 @@ public void ObjectsEqual() //Create data to be sent var expectedContent = GenerateTextContent(); var expectedData = Encoding.ASCII.GetBytes(expectedContent); + using var dataBuffer = ByteBuffer.AllocateDirect(expectedData.Length); + dataBuffer.WriteArray(0, expectedData, 0, expectedData.Length); try { - _normSession.DataEnqueue(expectedData, 0, expectedData.Length); + _normSession.DataEnqueue(dataBuffer, 0, expectedData.Length); var actualEvents = GetEvents(); From bc8ab6b8d08798863a5aae013dd980a7174198e9 Mon Sep 17 00:00:00 2001 From: mullerj Date: Fri, 23 Aug 2024 16:26:54 -0400 Subject: [PATCH 22/29] Updated NormFile.Name to use PtrToStringAnsi --- src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs index 0146f670..7a268250 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs @@ -31,6 +31,7 @@ public string Name { var buffer = new byte[FILENAME_MAX]; var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + string? name; try { @@ -39,14 +40,14 @@ public string Name { throw new IOException("Failed to get file name"); } + name = Marshal.PtrToStringAnsi(bufferPtr); } finally { bufferHandle.Free(); } - buffer = buffer.Where(c => c != 0).ToArray(); - return new string(buffer.Select(Convert.ToChar).ToArray()); + return name ?? string.Empty; } } From f04faca08ab56eb17614456c166cad2b9424f5e5 Mon Sep 17 00:00:00 2001 From: mullerj Date: Sun, 25 Aug 2024 13:25:13 -0400 Subject: [PATCH 23/29] Updated NormFile.Name to use stackalloc (#37) --- src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs | 23 ++++++-------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs index 7a268250..954e0846 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs @@ -25,28 +25,19 @@ internal NormFile(long handle) : base(handle) /// The name of the file. /// /// Thrown when failed to get file name. - public string Name + public unsafe string Name { get { - var buffer = new byte[FILENAME_MAX]; - var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - string? name; + var buffer = stackalloc byte[FILENAME_MAX]; + var bufferPtr = (nint)buffer; - try + if (!NormFileGetName(_handle, bufferPtr, FILENAME_MAX)) { - var bufferPtr = bufferHandle.AddrOfPinnedObject(); - if (!NormFileGetName(_handle, bufferPtr, FILENAME_MAX)) - { - throw new IOException("Failed to get file name"); - } - name = Marshal.PtrToStringAnsi(bufferPtr); - } - finally - { - bufferHandle.Free(); + throw new IOException("Failed to get file name"); } - + var name = Marshal.PtrToStringAnsi(bufferPtr); + return name ?? string.Empty; } } From 022e1204cad5bd6121e4d725b6ed5cde049b49e2 Mon Sep 17 00:00:00 2001 From: mullerj Date: Sun, 25 Aug 2024 14:39:24 -0400 Subject: [PATCH 24/29] Updated NormNode.Address to use stackalloc and create IPAddress from bytes --- src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs | 25 +++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs index 46dba7a1..19dcdb4a 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs @@ -1,5 +1,4 @@ -using Mil.Navy.Nrl.Norm.Buffers; -using System.Net; +using System.Net; using System.Runtime.InteropServices; namespace Mil.Navy.Nrl.Norm @@ -43,29 +42,21 @@ internal NormNode(long handle) /// /// The current network source address detected for packets received from remote NORM sender. /// - public IPEndPoint Address + public unsafe IPEndPoint Address { get { var bufferLength = 256; - using var buffer = ByteBuffer.AllocateDirect(bufferLength); - int port; + var buffer = stackalloc byte[bufferLength]; + var addrBuffer = (nint)buffer; - unsafe + if (!NormNodeGetAddress(_handle, addrBuffer, ref bufferLength, out int port)) { - byte* addrBuffer = null; - buffer.AcquirePointer(ref addrBuffer); - if (!NormNodeGetAddress(_handle, (nint)addrBuffer, ref bufferLength, out port)) - { - throw new IOException("Failed to get node address"); - } + throw new IOException("Failed to get node address"); } - var addressBytes = new byte[bufferLength]; - buffer.ReadArray(0, addressBytes, 0, bufferLength); - var ipAddressText = string.Join('.', addressBytes); - var ipAddress = IPAddress.Parse(ipAddressText); - + var addressBytes = new ReadOnlySpan(buffer, bufferLength); + var ipAddress = new IPAddress(addressBytes); return new IPEndPoint(ipAddress, port); } } From 32c3eb5239eac52e08c97d5f96d7fe8cdc875029 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 27 Aug 2024 21:06:13 -0400 Subject: [PATCH 25/29] NormObject Refactor * Updated NormObjectGetInfo to use byte array * Updated design --- src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml | 2 +- .../design/Mil/Navy/Nrl/Norm/NormFile.puml | 2 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 2 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs | 16 ++-------------- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index 824d07a8..1be3b84e 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -91,7 +91,7 @@ class NormApi <> { + {static} <> NormObjectGetType(objectHandle:long) : NormObjectType + {static} <> NormObjectHasInfo(objectHandle:long) : bool + {static} <> NormObjectGetInfoLength(objectHandle:long) : int - + {static} <> NormObjectGetInfo(objectHandle:long, buffer:nint, bufferLen:int) : int + + {static} <> NormObjectGetInfo(objectHandle:long, buffer:byte[], bufferLen:int) : int + {static} <> NormObjectGetSize(objectHandle:long) : int + {static} <> NormObjectGetBytesPending(objectHandle:long) : long + {static} <> NormObjectCancel(objectHandle:long) : void diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml index f26b63a9..b2e2a9b1 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormFile.puml @@ -2,7 +2,7 @@ class NormFile { + <> FILENAME_MAX : int = 260 <> NormFile(handle:long) - + Name : string <> + + <> Name : string <> + Rename(filePath:string) : void } NormObject <|-- NormFile diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index d1085efa..876941fc 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -961,7 +961,7 @@ public struct NormEvent /// function can be used to determine the length of NORM_INFO content for the object even if a NULL buffer value and /// zero bufferLen is provided. A zero value is returned if NORM_INFO content has not yet been received (or is nonexistent) for the specified object. [DllImport(NORM_LIBRARY)] - public static extern int NormObjectGetInfo(long objectHandle, nint buffer, int bufferLen); + public static extern int NormObjectGetInfo(long objectHandle, [Out] byte[] buffer, int bufferLen); /// /// This function can be used to determine the size (in bytes) of the transport object specified by the objectHandle parameter. diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs index f3ded3ae..1362e00d 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormObject.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace Mil.Navy.Nrl.Norm +namespace Mil.Navy.Nrl.Norm { /// /// The base transport object. @@ -44,17 +42,7 @@ public byte[]? Info var length = NormObjectGetInfoLength(_handle); var buffer = new byte[length]; - var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - - try - { - var bufferPtr = bufferHandle.AddrOfPinnedObject(); - NormObjectGetInfo(_handle, bufferPtr, length); - } - finally - { - bufferHandle.Free(); - } + NormObjectGetInfo(_handle, buffer, length); return buffer; } From 6ae32b1d0ea5722d02d5c011b6feaeb815fa08a8 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 27 Aug 2024 21:26:46 -0400 Subject: [PATCH 26/29] NormStream Refactor * Updated NormStream Write to use fixed statement * Updated NormStream Read to use fixed statement * Updated design --- .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 4 +-- src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 4 +-- .../src/Mil.Navy.Nrl.Norm/NormStream.cs | 30 +++++++------------ 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index 1be3b84e..1cdc0091 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -56,7 +56,7 @@ class NormApi <> { + {static} <> NormRequeueObject(sessionHandle:long, objectHandle:long) : bool + {static} <> NormStreamOpen(sessionHandle:long, bufferSize:long, infoPtr:nint, infoLen:int) : long + {static} <> NormStreamClose(streamHandle:long, graceful:bool) : void - <> {static} <> NormStreamWrite(streamHandle:long, buffer:nint, numBytes:int) : int + + <> {static} <> NormStreamWrite(streamHandle:long, buffer:byte*, numBytes:int) : int + {static} <> NormStreamFlush(streamHandle:long, eom:bool, flushMode:NormFlushMode) : void + {static} <> NormStreamSetAutoFlush(streamHandle:long, flushMode:NormFlushMode) : void + {static} <> NormStreamSetPushEnable(streamHandle:long, pushEnable:bool) : void @@ -85,7 +85,7 @@ class NormApi <> { + {static} <> NormNodeSetRepairBoundary(remoteSender:long, repairBoundary:NormRepairBoundary) : void + {static} <> NormSetDefaultRxRobustFactor(sessionHandle:long, robustFactor:int) : void + {static} <> NormNodeSetRxRobustFactor(remoteSender:long, robustFactor:int) : void - + {static} <> NormStreamRead(streamHandle:long, buffer:nint, numBytes:int) : bool + + <> {static} <> NormStreamRead(streamHandle:long, buffer:byte*, numBytes:int) : bool + {static} <> NormStreamSeekMsgStart(streamHandle:long) : bool + {static} <> NormStreamGetReadOffset(streamHandle:long) : long + {static} <> NormObjectGetType(objectHandle:long) : NormObjectType diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index 876941fc..ab639364 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -591,7 +591,7 @@ public struct NormEvent /// The numBytes parameter indicates the length of the data content. /// This function returns the number of bytes of data successfully enqueued for NORM stream transmission. [DllImport(NORM_LIBRARY)] - internal static extern int NormStreamWrite(long streamHandle, nint buffer, int numBytes); + public unsafe static extern int NormStreamWrite(long streamHandle, byte* buffer, int numBytes); /// /// This function causes an immediate "flush" of the transmit stream specified by the streamHandle parameter. @@ -895,7 +895,7 @@ public struct NormEvent /// This function normally returns a value of true. However, if a break in the integrity of the reliable received stream /// occurs(or the stream has been ended by the sender), a value of false is returned to indicate the break. [DllImport(NORM_LIBRARY)] - public static extern bool NormStreamRead(long streamHandle, nint buffer, ref int numBytes); + public unsafe static extern bool NormStreamRead(long streamHandle, byte* buffer, ref int numBytes); /// /// This function advances the read offset of the receive stream referenced by the streamHandle parameter to align diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs index c6b39d18..a0eb3658 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs @@ -59,16 +59,12 @@ public int Write(byte[] buffer, int offset, int length) } int numBytes; - var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - - try - { - var bufferPtr = bufferHandle.AddrOfPinnedObject() + offset; - numBytes = NormStreamWrite(_handle, bufferPtr, length); - } - finally + unsafe { - bufferHandle.Free(); + fixed (byte* bufferPtr = buffer) + { + numBytes = NormStreamWrite(_handle, bufferPtr + offset, length); + } } return numBytes; @@ -158,20 +154,16 @@ public int Read(byte[] buffer, int offset, int length) throw new ArgumentOutOfRangeException(nameof(length), "The length is out of range"); } - var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - - try + unsafe { - var bufferPtr = bufferHandle.AddrOfPinnedObject() + offset; - if (!NormStreamRead(_handle, bufferPtr, ref length)) + fixed (byte* bufferPtr = buffer) { - length = -1; + if (!NormStreamRead(_handle, bufferPtr + offset, ref length)) + { + length = -1; + } } } - finally - { - bufferHandle.Free(); - } return length; } From e4513ccd53ac2ed5605a1bf01d1c4558d796c8ae Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 27 Aug 2024 21:40:12 -0400 Subject: [PATCH 27/29] NormNode Refactor * Updated NormNodeGetAddress to use byte* to avoid casting * Updated NormNode GetCommand to use fixed statement * Updated design --- .../design/Mil/Navy/Nrl/Norm/NormApi.puml | 4 ++-- src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 4 ++-- src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs | 21 +++++++------------ .../src/Mil.Navy.Nrl.Norm/NormStream.cs | 4 +--- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index 1cdc0091..bddbac54 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -102,9 +102,9 @@ class NormApi <> { + {static} <> NormDataAccessData(objectHandle:long) : nint + {static} <> NormObjectGetSender(objectHandle:long) : long + {static} <> NormNodeGetId(nodeHandle:long) : long - + {static} <> NormNodeGetAddress(nodeHandle:long, addrBuffer:nint, bufferLen:int, port:int) : bool + + <> {static} <> NormNodeGetAddress(nodeHandle:long, addrBuffer:byte*, bufferLen:int, port:int) : bool + {static} <> NormNodeGetGrtt(nodeHandle:long) : double - + {static} <> NormNodeGetCommand(remoteSender:long, cmdBuffer:nint, buflen:int) : bool + + <> {static} <> NormNodeGetCommand(remoteSender:long, cmdBuffer:byte*, buflen:int) : bool + {static} <> NormNodeFreeBuffers(remoteSender:long) : void + {static} <> NormNodeRetain(nodeHandle:long) : void + {static} <> NormNodeRelease(nodeHandle:long) : void diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index ab639364..d150d996 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -1081,7 +1081,7 @@ public struct NormEvent /// port number and/or specify a specific source address binding that is used for packet transmission. /// A value of true is returned upon success and false upon failure. An invalid nodeHandle parameter value would lead to such failure. [DllImport(NORM_LIBRARY)] - public static extern bool NormNodeGetAddress(long nodeHandle, nint addrBuffer, ref int bufferLen, out int port); + public unsafe static extern bool NormNodeGetAddress(long nodeHandle, byte* addrBuffer, ref int bufferLen, out int port); /// /// This function retrieves the advertised estimate of group round-trip timing (GRTT) for the remote sender referenced by the given nodeHandle value. @@ -1104,7 +1104,7 @@ public struct NormEvent /// either no command was available or the provided buffer size (buflen parameter) was inadequate. /// The value referenced by the buflen parameter is adjusted to indicate the actual command length (in bytes) upon return. [DllImport(NORM_LIBRARY)] - public static extern bool NormNodeGetCommand(long remoteSender, nint cmdBuffer, ref int buflen); + public unsafe static extern bool NormNodeGetCommand(long remoteSender, byte* cmdBuffer, ref int buflen); /// /// This function releases memory resources that were allocated for a remote sender. diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs index 19dcdb4a..dc8dc95d 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormNode.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Runtime.InteropServices; namespace Mil.Navy.Nrl.Norm { @@ -48,9 +47,8 @@ public unsafe IPEndPoint Address { var bufferLength = 256; var buffer = stackalloc byte[bufferLength]; - var addrBuffer = (nint)buffer; - if (!NormNodeGetAddress(_handle, addrBuffer, ref bufferLength, out int port)) + if (!NormNodeGetAddress(_handle, buffer, ref bufferLength, out int port)) { throw new IOException("Failed to get node address"); } @@ -81,20 +79,17 @@ public int GetCommand(byte[] buffer, int offset, int length) throw new ArgumentOutOfRangeException(nameof(length), "The length is out of range"); } - var bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - - try + unsafe { - var bufferPtr = bufferHandle.AddrOfPinnedObject() + offset; - if (!NormNodeGetCommand(_handle, bufferPtr, ref length)) + fixed (byte* bufferPtr = buffer) { - throw new IOException("Failed to get command"); + if (!NormNodeGetCommand(_handle, bufferPtr + offset, ref length)) + { + throw new IOException("Failed to get command"); + } } - } - finally - { - bufferHandle.Free(); } + return length; } diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs index a0eb3658..1b66637d 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormStream.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace Mil.Navy.Nrl.Norm +namespace Mil.Navy.Nrl.Norm { /// /// A transport object of type NORM_OBJECT_STREAM. From 3ee6345fd0573e67173ad856e37c77aca2b39686 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 27 Aug 2024 22:18:45 -0400 Subject: [PATCH 28/29] NormFile Refactor * Updated NormFileGetName to use sbyte* to avoid casting * Updated design --- src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml | 2 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs | 2 +- src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs | 15 +++++---------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml index bddbac54..508dbe8a 100644 --- a/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml +++ b/src/dotnet/design/Mil/Navy/Nrl/Norm/NormApi.puml @@ -97,7 +97,7 @@ class NormApi <> { + {static} <> NormObjectCancel(objectHandle:long) : void + {static} <> NormObjectRetain(objectHandle:long) : void + {static} <> NormObjectRelease(objectHandle:long) : void - + {static} <> NormFileGetName(fileHandle:long, nameBuffer:nint, bufferLen:int) : bool + + <> {static} <> NormFileGetName(fileHandle:long, nameBuffer:sbyte*, bufferLen:int) : bool + {static} <> NormFileRename(fileHandle:long, fileName:string) : bool + {static} <> NormDataAccessData(objectHandle:long) : nint + {static} <> NormObjectGetSender(objectHandle:long) : long diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs index d150d996..341ccdaf 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormApi.cs @@ -1020,7 +1020,7 @@ public struct NormEvent /// does not refer to an object of type NORM_OBJECT_FILE. /// [DllImport(NORM_LIBRARY)] - public static extern bool NormFileGetName(long fileHandle, nint nameBuffer, int bufferLen); + public unsafe static extern bool NormFileGetName(long fileHandle, sbyte* nameBuffer, int bufferLen); /// /// This function renames the file used to store content for the NORM_OBJECT_FILE transport object specified by diff --git a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs index 954e0846..bb95a47e 100644 --- a/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs +++ b/src/dotnet/src/Mil.Navy.Nrl.Norm/NormFile.cs @@ -1,7 +1,4 @@ -using System.Runtime.InteropServices; -using System.Text; - -namespace Mil.Navy.Nrl.Norm +namespace Mil.Navy.Nrl.Norm { /// /// A transport object of type NORM_OBJECT_FILE. @@ -29,16 +26,14 @@ public unsafe string Name { get { - var buffer = stackalloc byte[FILENAME_MAX]; - var bufferPtr = (nint)buffer; + var buffer = stackalloc sbyte[FILENAME_MAX]; - if (!NormFileGetName(_handle, bufferPtr, FILENAME_MAX)) + if (!NormFileGetName(_handle, buffer, FILENAME_MAX)) { throw new IOException("Failed to get file name"); } - var name = Marshal.PtrToStringAnsi(bufferPtr); - - return name ?? string.Empty; + + return new string(buffer); } } From bb799b585080b81bb7f3945428e54ed667b40df1 Mon Sep 17 00:00:00 2001 From: mullerj Date: Tue, 11 Feb 2025 19:00:26 +0000 Subject: [PATCH 29/29] Added workflow_dispatch to workflows --- .github/workflows/dotnet-publish.yml | 1 + .github/workflows/dotnet.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-publish.yml b/.github/workflows/dotnet-publish.yml index 8f66cbc7..15ffb13f 100644 --- a/.github/workflows/dotnet-publish.yml +++ b/.github/workflows/dotnet-publish.yml @@ -8,6 +8,7 @@ on: branches: - master - main + workflow_dispatch: jobs: publish: diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 4bb2d85c..4747842d 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,6 +1,6 @@ name: .NET -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: build: