dimas 0.5.1

dimas - a framework for Distributed Multi Agent Systems
Documentation
# dimas

[DiMAS](https://github.com/dimas-fw/dimas/tree/main/dimas) - A framework
for building **Di**stributed **M**ulti **A**gent **S**ystems

⚠️ WARNING ⚠️ : `DiMAS` is under active development,
so expect gaps between implementation and documentation.

A distributed multi agent system is a set of independant agents
that are widely distributed but somehow connected.
They are designed in a way that they can solve complex tasks by working together.

The system is characterised by

- a somewhat large and complex environment
- containing a set of (non agent) objects that can be perceived, created, moved,
modified or destroyed by the agents
- that changes over time due to external rules

with multiple agents operating in that environment which

- can perceive the environment to a limited extent
- have the possibility to communicate with some or all of the other agents
- have certain capabilities to influence the environment

This crate is available on [crates.io](https://crates.io/crates/dimas).

[DiMAS](https://github.com/dimas-fw/dimas/tree/main/dimas) follows the semantic
versioning principle with the enhancement, that until version 1.0.0
each new minor version has breaking changes, while patches are non breaking
changes but may include enhancements.

## Usage

' ' uses the `tokio` runtime, you have to define your `main` function as an
`async` function. The declaration of tokio crate is not necessary, unless you use
tokio functionality within your implementations.

So include `dimas` runtime in the dependencies section of
your `Cargo.toml`.

Your `Cargo.toml` should include:

```toml
[dependencies]
dimas = "0.5"
```

It makes sense to return a `Result`, as most `DiMAS` `Agent`s functions return one.
`DiMAS` errors are of type `Box<dyn core::error::Error>` and must be thread safe.
`DiMAS` provides a type definition `Result<T>` to make life easier

`DiMAS` also provides a `main` attribute macro to create the runtime environment
and a `prelude` to import most used declarations.

A suitable main program skeleton may look like:

```rust
use dimas::prelude::*;

#[dimas::main]
async fn main() -> Result<()> {

    // your code
    // ...

    Ok(())
}
```

## Example

A very simple example consist at least of two agents, a `publisher` publishing messages
and a `subscriber` that is listening to those messages.

The `Cargo.toml` for this publisher/subscriber example should include

```toml
[dependencies]
dimas = version = "0.5"
```

### Publisher

The `publisher.rs` should look like this:

```rust,no_run
use dimas::prelude::*;
use core::time::Duration;

/// The Agent's properties
#[derive(Debug)]
struct AgentProps {
    counter: u128,
}

#[dimas::main]
async fn main() -> Result<()> {
    // create & initialize agents properties
    let properties = AgentProps { counter: 0 };

    // create an agent with the properties and default configuration
    let agent = Agent::new(properties)
       .config(&Config::default())?;

    // create publisher for topic "hello"
    agent
        .publisher()
        .topic("hello")
        .add()?;

    // use a timer for regular publishing of "hello" topic
    agent
        // get the TimerBuilder from the agent
        .timer()
        // set a name for the timer
        .name("timer")
        // every second
        .interval(Duration::from_secs(1))
        // the timers callback function as a closure
        .callback(
            |ctx| -> Result<()> {
                let counter = ctx
                    .read()?
                    .counter;
                // the message to send
                let text = format!("Hello World! [{counter}]");
                // just to see what will be sent
                println!("Sending '{}'", &text);
                // publishing with stored publisher for topic "hello"
                let message = Message::encode(&text);
                ctx.put("hello", message)?;
                // modify counter in properties
                ctx
                    .write()?
                    .counter += 1;
                Ok(())
            }
        )
        // finally add the timer to the agent
        // errors will be propagated to main
        .add()?;

    // start the agent
    agent.start().await?;
    Ok(())
}
```

### Subscriber

The `subscriber.rs` should look like this:

```rust,no_run
use dimas::prelude::*;

/// The Agent's properties
#[derive(Debug)]
pub struct AgentProps {}

async fn callback(_ctx: Context<AgentProps>, message: Message) -> Result<()> {
    let message: String = message.decode()?;
    println!("Received '{message}'");
    Ok(())
}

#[dimas::main]
async fn main() -> Result<()> {
    // create & initialize agents properties
    let properties = AgentProps {};

    // create an agent with the properties and default configuration
    let agent = Agent::new(properties)
        .config(&Config::default())?;

    // subscribe to "hello" messages
    agent
        // get the SubscriberBuilder from the agent
        .subscriber()
        //set wanted message topic (corresponding to publishers topic!)
        .topic("hello")
        // set the callback function for put messages
        .put_callback(callback)
        // finally add the subscriber to the agent
        // errors will be propagated to main
        .add()?;

    // start the agent
    agent.start().await?;
    Ok(())
}
```

## More examples

You can find some simple examples in [dimas-fw/dimas/examples](https://github.com/dimas-fw/dimas/blob/main/examples/README.md)
and more complex examples in [dimas-fw/examples](https://github.com/dimas-fw/examples/blob/main/README.md)

## Features

- unstable: Enables the unstable features.

## License

Licensed with the fair use "NGMC" license, see [license file](https://github.com/dimas-fw/dimas/blob/main/LICENSE)

## Contribution

Any contribution intentionally submitted for inclusion in the work by you,
shall be licensed with the same "NGMC" license, without any additional terms or conditions.