Skip to content

Commit

Permalink
Merge pull request #35 from MattSScott/readme-update
Browse files Browse the repository at this point in the history
Update Docs: `ReadMe` and `User Manual`
  • Loading branch information
MattSScott authored Oct 22, 2024
2 parents 5fef429 + 84ae8cc commit 9137bea
Show file tree
Hide file tree
Showing 13 changed files with 279 additions and 73 deletions.
File renamed without changes.
154 changes: 153 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,155 @@
# basePlatformSOMAS

Base platform for ICL SOMAS course
<!-- Base platform for ICL SOMAS course -->

<a id="readme-top"></a>

[![Contributors][contributors-shield]][contributors-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]

<!-- TABLE OF CONTENTS -->
<details>
<summary>Table of Contents</summary>
<ol>
<li>
<a href="#about-the-project">About The Project</a>
</li>
<li>
<a href="#getting-started">Getting Started</a>
<ul>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#installation">Installation</a></li>
</ul>
</li>
<li><a href="#usage">Usage</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#license">License</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#acknowledgments">Acknowledgments</a></li>
</ol>
</details>

<!-- ABOUT THE PROJECT -->

## About The Project

basePlatformSOMAS is a generic 'base platform', in the form of a package, used to simplify the development of a self-organising, multi-agent system (SOMAS). This package provides a set of core 'building block' components:

- A 'base server' - an extensible server object for managing the environmental gamestate and regulating the internal state of...
- A 'base agent' - an extensible (multi-)agent object that can be injected into the server for simulation, and abstracts the core functions for agent-to-agent interactions through...
- A 'base message' - an extensible messaging component that helps to define a common language for multi-agent communication.

These building blocks come with 1. an interface definition for defining the core functions required for an operational multi-agent system and 2. the building block object itself, to provide a base implementation of these core functions, and allow extension.

For example, the `IServer` interface contains a set of functions that all servers in any multi-agent system must perform (the ability to add or remove agents, say), and the `BaseServer` object will give a base implementation of these methods.

It is then left to the user of this package to define an `ExtendedServer` which adds all of the relevant functionality for their multi-agent scenario, while composing the `BaseServer` to get access to the methods provided by this package.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- GETTING STARTED -->

## Getting Started

To begin working with basePlatformSOMAS, follow these simple example steps.

### Prerequisites

This package is entirely build in `GoLang (go)`. Your machine therefore needs a golang compiler installed. The relevant download link can be found [here](https://go.dev/doc/install).

[![Golang][go-shield]][go-url]

Since we have defined this repository as a package, it must be included in an existing `go` project. As such, you should be initialising a `go` repository in a folder of your choice with:

```sh
go mod init <your-module-name>
```

### Installation

With a working `go` repository, you can now include our package:

```sh
go get github.com/MattSScott/basePlatformSOMAS/v2
```

and, for peace of mind, tidy the imports:

```sh
go mod tidy
```

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- USAGE EXAMPLES -->

## Usage

You will now have access to the modules provided by this package:

- The _server_ module can be imported into a file with: `github.com/MattSScott/basePlatformSOMAS/v2/pkg/server`
- The _agent_ module can be imported into a file with: `github.com/MattSScott/basePlatformSOMAS/v2/pkg/agent`
- The _message_ module can be imported into a file with: `github.com/MattSScott/basePlatformSOMAS/v2/pkg/message`

For more examples, and a **much, much more detailed write-up** of the package, please refer to the [User Manual](https://github.com/MattSScott/basePlatformSOMAS/blob/main/basePlatformSOMASv2.0.pdf) or, if you're using an outdated version of the package, refer to the [Past Manuals](https://github.com/MattSScott/basePlatformSOMAS/tree/main/Past%20Manuals).

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- CONTRIBUTING -->

## Contributing

basePlatformSOMAS is an open source project made for SOMAS programmers, by SOMAS programmers. Any contributions you make are **greatly appreciated**.

If you have a suggestion that would make this package better, please fork the repo and create a pull request. If you would rather leave the implementation of this suggestion to us, you can also simply open an issue with the tag "enhancement".

To make a pull request in a helpful way:

1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- LICENSE -->

## License

Distributed under the MIT License. See `LICENSE.txt` for more information.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- CONTACT -->

## Contact

[Matthew Scott](https://profiles.imperial.ac.uk/matthew.scott18) - matthew.scott18@imperial.ac.uk

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- ACKNOWLEDGMENTS -->

## Acknowledgments

This work was produced by the following developers:

- [Matthew Scott](https://github.com/MattSScott)
- [Ana Dimoska](https://github.com/ADimoska)
- [Mikayel Suvaryan](https://github.com/mika111)

<p align="right">(<a href="#readme-top">back to top</a>)</p>

<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->

[contributors-shield]: https://img.shields.io/github/contributors/MattSScott/basePlatformSOMAS.svg
[contributors-url]: https://github.com/MattSScott/basePlatformSOMAS/graphs/contributors
[issues-shield]: https://img.shields.io/github/issues/MattSScott/basePlatformSOMAS.svg?color=orange
[issues-url]: https://github.com/MattSScott/basePlatformSOMAS/graphs/issues
[license-shield]: https://img.shields.io/github/license/MattSScott/basePlatformSOMAS.svg
[license-url]: https://github.com/MattSScott/basePlatformSOMAS/blob/main/LICENSE.txt
[go-shield]: https://img.shields.io/badge/GoLang-blue?logo=go
[go-url]: https://go.dev
Binary file added basePlatformSOMASv2.0.pdf
Binary file not shown.
12 changes: 9 additions & 3 deletions internal/diagnosticsEngine/diagnosticsEngine.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type IDiagnosticsEngine interface {
// allow agents to report status of sent message
ReportSendMessageStatus(bool)
// allow server to report number of end message closures
ReportEndMessagingStatus()
ReportEndMessagingStatus(int)
// allow for resetting of diagnostics for round-to-round data
ResetRoundDiagnostics()
// compile results for end of round messaging status
Expand All @@ -33,8 +33,8 @@ func (de *DiagnosticsEngine) ReportSendMessageStatus(status bool) {
}
}

func (de *DiagnosticsEngine) ReportEndMessagingStatus() {
de.numEndMessagings++
func (de *DiagnosticsEngine) ReportEndMessagingStatus(n int) {
de.numEndMessagings = n
}

func (de *DiagnosticsEngine) ResetRoundDiagnostics() {
Expand Down Expand Up @@ -68,9 +68,15 @@ func (de *DiagnosticsEngine) GetNumberMessageDrops() int {
}

func (de *DiagnosticsEngine) GetMessagingSuccessRate() float32 {
if de.numMessages == 0 {
return 100
}
return 100 * float32(de.numMessageSuccesses) / float32(de.numMessages)
}

func (de *DiagnosticsEngine) GetEndMessagingSuccessRate(numAgents int) float32 {
if numAgents == 0 {
return 100
}
return 100 * float32(de.numEndMessagings) / float32(numAgents)
}
26 changes: 18 additions & 8 deletions internal/diagnosticsEngine/diagnosticsEngine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestGetNumberEndMessagings(t *testing.T) {
if initEnds != 0 {
t.Error("Diagnostics engine intialised with non-zero number")
}
engine.ReportEndMessagingStatus()
engine.ReportEndMessagingStatus(1)
newEnds := engine.GetNumberEndMessagings()
if newEnds != initEnds+1 {
t.Error("Diagnostics engine successes not correctly incremented")
Expand Down Expand Up @@ -94,11 +94,8 @@ func TestEndMessagingSuccessRate(t *testing.T) {
engine := diagnosticsEngine.CreateDiagnosticsEngine()
numAgents := 100
numReports := 20
for i := 0; i < numAgents; i++ {
if i < numReports {
engine.ReportEndMessagingStatus()
}
}

engine.ReportEndMessagingStatus(numReports)
trueSuccessRate := engine.GetEndMessagingSuccessRate(numAgents)
expectedSuccessRate := float32(numReports) / float32(numAgents) * 100
if trueSuccessRate != expectedSuccessRate {
Expand All @@ -109,11 +106,12 @@ func TestEndMessagingSuccessRate(t *testing.T) {
func TestResetDiagnostics(t *testing.T) {
engine := diagnosticsEngine.CreateDiagnosticsEngine()
rounds := 3
nMessages := 10
for r := 0; r < rounds; r++ {
for delta := 0; delta < 10; delta++ {
engine.ReportEndMessagingStatus()
for delta := 0; delta < nMessages; delta++ {
engine.ReportSendMessageStatus(true)
}
engine.ReportEndMessagingStatus(nMessages)
engine.ResetRoundDiagnostics()
nSent := engine.GetNumberSentMessages()
nSucc := engine.GetNumberMessageSuccesses()
Expand All @@ -123,3 +121,15 @@ func TestResetDiagnostics(t *testing.T) {
}
}
}

func TestDivideByZeroProtection(t *testing.T) {
engine := diagnosticsEngine.CreateDiagnosticsEngine()
msgSuccessRate := engine.GetMessagingSuccessRate()
endMsgingSuccessRate := engine.GetEndMessagingSuccessRate(0)
if msgSuccessRate != 100.0 {
t.Errorf("Diagnostic Engine incorrectly reported Message Success rate when 0 messages sent. Expected 100%%, got %v%%", msgSuccessRate)
}
if endMsgingSuccessRate != 100.0 {
t.Errorf("Diagnostic Engine incorrectly reported Finished Messaging Success rate when 0 agents are present. Expected 100%%, got %v%%", endMsgingSuccessRate)
}
}
14 changes: 5 additions & 9 deletions internal/testUtils/testUtilsAgent.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type ITestBaseAgent interface {
GetGoal() int32
SetGoal(int32)
FinishedMessaging()
NotifyAgentFinishedMessagingUnthreaded(*sync.WaitGroup, *uint32)
SignalMessagingCompleteUnthreaded(*sync.WaitGroup, *uint32)
GetAgentStoppedTalking() int
HandleTimeoutTestMessage(msg TestTimeoutMessage)
HandleInfiniteLoopMessage(msg TestMessagingBandwidthLimiter)
Expand All @@ -32,13 +32,9 @@ type TestServerFunctionsAgent struct {
*agent.BaseAgent[ITestBaseAgent]
}

func (ta *TestServerFunctionsAgent) UpdateAgentInternalState() {
ta.Counter += 1
}

func (ta *TestServerFunctionsAgent) FinishedMessaging() {
ta.StoppedTalking++
ta.NotifyAgentFinishedMessaging()
ta.SignalMessagingComplete()
}

func (ta *TestServerFunctionsAgent) CreateTestMessage() *TestMessage {
Expand All @@ -48,7 +44,7 @@ func (ta *TestServerFunctionsAgent) CreateTestMessage() *TestMessage {
}
}

func (ta *TestServerFunctionsAgent) NotifyAgentFinishedMessagingUnthreaded(wg *sync.WaitGroup, counter *uint32) {
func (ta *TestServerFunctionsAgent) SignalMessagingCompleteUnthreaded(wg *sync.WaitGroup, counter *uint32) {
defer wg.Done()
ta.AgentStoppedTalking(ta.GetID())
atomic.AddUint32(counter, 1)
Expand All @@ -61,7 +57,7 @@ func (ta TestServerFunctionsAgent) GetAgentStoppedTalking() int {
func (ta *TestServerFunctionsAgent) HandleTestMessage() {
newCounterValue := atomic.AddInt32(&ta.Counter, 1)
if newCounterValue == atomic.LoadInt32(&ta.Goal) {
ta.NotifyAgentFinishedMessaging()
ta.SignalMessagingComplete()
}
}

Expand Down Expand Up @@ -104,7 +100,7 @@ func (ta *TestServerFunctionsAgent) HandleTimeoutTestMessage(msg TestTimeoutMess
start := time.Now()
time.Sleep(msg.Workload) // simulate long work
fmt.Println("work has been completed, took ", time.Since(start), "notifying finished messaging")
ta.NotifyAgentFinishedMessaging()
ta.SignalMessagingComplete()
}

func (ta *TestServerFunctionsAgent) HandleInfiniteLoopMessage(msg TestMessagingBandwidthLimiter) {
Expand Down
11 changes: 4 additions & 7 deletions internal/testUtils/testUtilsServer.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type TestServer struct {

func GenerateTestServer(numAgents, iterations, turns int, maxDuration time.Duration, maxThreads int) *TestServer {
serv := &TestServer{
BaseServer: server.CreateServer[ITestBaseAgent](iterations, turns, maxDuration, maxThreads),
BaseServer: server.CreateBaseServer[ITestBaseAgent](iterations, turns, maxDuration, maxThreads),
TurnCounter: 0,
IterationStartCounter: 0,
IterationEndCounter: 0,
Expand Down Expand Up @@ -58,17 +58,14 @@ func NewTestMessage() *TestMessage {
}

func (ts *TestServer) RunTurn(turn, iteration int) {
for _,ag:= range ts.GetAgentMap() {
for _, ag := range ts.GetAgentMap() {
newMsg := ag.CreateTestMessage()
ag.BroadcastMessage(newMsg)
}

ts.TurnCounter += 1
}




func (ts *TestServer) RunStartOfIteration(iteration int) {
ts.IterationStartCounter += 1
}
Expand All @@ -80,6 +77,6 @@ func (ts *TestServer) RunEndOfIteration(iteration int) {
func SendNotifyMessages(agMap map[uuid.UUID]ITestBaseAgent, count *uint32, wg *sync.WaitGroup) {
for _, ag := range agMap {
wg.Add(1)
go ag.NotifyAgentFinishedMessagingUnthreaded(wg, count)
go ag.SignalMessagingCompleteUnthreaded(wg, count)
}
}
12 changes: 6 additions & 6 deletions pkg/agent/agentInterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ type IExposedServerFunctions[T any] interface {
}

type IMessagingFunctions[T any] interface {
// signals end of agent's listening session
NotifyAgentFinishedMessaging()
// allows for creation of a base message
CreateBaseMessage() message.BaseMessage
// allows for sending a message across the entire system
BroadcastMessage(message.IMessage[T])
// allows for sending a message to a single recipient
SendMessage(message.IMessage[T], uuid.UUID)
// allows for sending a message to a single recipient synchronously
SendSynchronousMessage(message.IMessage[T], uuid.UUID)
// allows for sending an async message across the entire system
BroadcastMessage(message.IMessage[T])
// allows for sending a sync message across the entire system
BroadcastSynchronousMessage(message.IMessage[T])
// signals end of agent's listening session
SignalMessagingComplete()
}

type IAgent[T any] interface {
Expand All @@ -41,6 +43,4 @@ type IAgent[T any] interface {
IMessagingFunctions[T]
// returns the unique ID of an agent
GetID() uuid.UUID
// allows agent to update their internal state
UpdateAgentInternalState()
}
Loading

0 comments on commit 9137bea

Please sign in to comment.