Ciprus
Lightweight flow-based runtime for simulation and automation
Name is the acronym of Circuit Programming Runtime for Simulation.
Originally created as a lightweight alternative to Apache Camel for simulation. Later the design changed focus to create lightweight home automation runtime supporting visual design inspired by Unreal Blueprints. The working of the nodes inspired by Akka's (Akka.io) actor model. The core of the runtime inspired by the Erlang BEAM and OTP to support modern needs like fault-tolerancy and scalability without compromising on the straightforward API.
Features
- Use transparent message structure for data sharing between the nodes
- Use a clean straightforward API to manage the flows at scale
(Inside of one process or between processes with message queues)
- Optimized for speed and low resource usage
- Object oriented design so nodes can have states and methods
- By design supports creating or changing flows on the fly (Not tested yet)
- Scalable and fault tolerant
(the business logic is running on separated green thread for each node)
- With optional back pressure settings
- Uses only in-memory database for configuration (SQLite without cgo)
- Portable and even supports cross-compilation
(fully written in go without cgo)
- By design easily testable and debuggable (test coverage is bigger than 80%)
- Ready for IOT usage if needed (a minimal server is ready)
- Designed to support multiple languages for business logic via WASI
- Has its own extensible scripting language via WASI (experimental without parallel support)
PROJECT STATUS: 1.0b
Usage
At first we have to create a context like
var CONFIG_PATH = "conf.json"
var SimpleLogger = utils.NewBasicLogger(utils.LoggerConfig{Level: utils.DebugLevel}, "")
ctx := runtime.NewContext(CONFIG_PATH, SimpleLogger)
defer ctx.Shutdown() // To call Terminate() on all nodes
Then custom messages can be used to wrap data types used for messaging
ctx.RegisterMessage("PushEventMsg", func() runtime.MessageIFace {
return messages.NewBasicTypedMessage(NewPushEvent)
})
The nodes have to be registered before use
ctx.RegisterNode("PushListener", NewSimplePushListener)
ctx.RegisterNode("Lamp", NewLamp)
Then they can be created as part of a flow
flow := ctx.AddFlow("PushHandlerFlow")
pushListener := flow.AddNode("PushListener")
lamp := flow.AddNode("Lamp")
After that nodes can be connected to be used as part of a flow
link := flow.Connect(pushListener, "Out", lamp, "In")
link.SetWaitSettings(runtime.WaitSettings{Condition: runtime.WaitForCaller})
And then the new flow can be started like that
ctx.RunFlowByNameWithWait(&config, "PushHandlerFlow", startMessage)
Links for more info about the used concepts and features