Writing a plugin
Let's write a trivial plugin in go
Note that this document is written pre-release. Details, particularly around the SDK, are subject to change.
One of the key features of rtd.pub is the ability to use any language to implement your integration with Excel. Perhaps you have an existing system that you want to extend, or simply prefer writing code in a particular language.
We really want to avoid boilerplate and make it as easy as possible, so that you can focus on adding value... as a PHB may say.

To show how easy it is, we're going to build a trivial plugin that counts from 0 to 60. Amazing, right? Not really, but hopefully this will give you a taste for how easy it is to do far more meaningful things.
Implementation
Create a go module and in main.go
import some modules. (Note that these will be available when rtd.pub is generally available.)
Create a struct with an embedded PluginServer
which handles the communication between your plugin and rtd.pub.
package main
import
(
"github.com/acornsoftuk/plugin"
pb "github.com/acornsoftuk/plugin/protos"
"github.com/sirupsen/logrus"
)
type CountingPlugin struct {
plugin.PluginBase
}
Next, some hooks to handle plugin lifecycle.
func (p *CountingPlugin) Run(ctx context.Context) error {
logrus.Infof("CountingPlugin started")
return nil
}
func (p *CountingPlugin) Stop(context.Context) {
logrus.Infof("CountingPlugin stopped")
}
We want our counter to start when a user adds a rtdPub.Sub
call to a formula cell. Implementing Subscribe
starts the subscription to a given piece of data, which will ultimately land back into that cell.
The Subscribe
func expects the topic ID (assigned by Excel), subject and additional parameters. Most important is the setResult
callback that ensures a new value is written to the correct topic.
Plugins must respect the context ctx
passed by the runtime, and terminate when it is cancelled. This ensures that values cease to be produced when the subscribe cells are de
func (p *CountingPlugin) Subscribe(ctx context.Context, tid int32, subj string, params []string,
setResult plugin.SendFunc) error {
go func() {
// extract/parse from []params slice using type of default
// params, index, default
maxTicks := plugin.TryParam(params, 0, 60)
delay := plugin.TryParam(params, 1, 1)
ticker := time.NewTicker(time.Duration(delay) * time.Second)
defer ticker.Stop()
for i := 0; i <= maxTicks; i++ {
select {
case <-ctx.Done():
log.Infof("context cancelled, stopping counter goroutine.")
return
case <-ticker.C:
if err := setResult(i); err != nil {
log.Error(err)
}
}
}
log.Infof("counter reached %v, stopping goroutine for topic %v.", maxTicks, tid)
}()
log.Infof("counting plugin got subscribe: %v", tid)
return nil
}
Unsubscribe is called when there are no more cells referencing the topic. Implementing this is optional, but it is a good place to tidy up any external subscriptions.
func (p *CountingPlugin) Unsubscribe(topicId int32) error {
logrus.Infof("got unsubscribe: %v", req)
}
A plugin is an executable. Not much more to do now, other than add a main func tp start the thing up.
func main() {
// starts a gRPC server
plugin.Start(&CountingPlugin{})
}
And build it (for Windows)
GOOS=windows go build -o rtdpi-demo.exe
This should yield rtdpi-demo.exe
Installation
Move or copy the .exe to the plugins
folder where you have installed rtd.pub.
Edit rtdpub.yaml
to look similar to the below. This creates a connector, which is a reified instance of a plugin. This plugin does not have any configuration values, but if it did, these could be added to the properties
map.
Also of note is the optional streams:
section. This allows you to alias the rtdPub.Sub
function to something more user friendly and discoverable by end users, complete with documentation.
rtdpub:
plugins:
path: ./plugins
connectors:
tutorial:
uses: demo
properties: {}
streams:
- name: Counter
params:
- name: max
description: Number to count up to
Try it out
Start Excel and create a blank workbook.
Add a formula in any cell like =rtdPub.Sub("tutorial", "counter")
Stand back in amazement as the value updates.
While this is happening add another, =rtdPub.Sub("tutorial", "counter2")
and watch the two cells update in unison. You can fill up or down to reference the same data point in multiple places through the same subscription.
You can see it running in the below video.
Wrap up
Easy! Python support is coming soon.
If you have any questions along the way, we are here to help. Drop us an email. You can also register for early access.
Last updated