Documentation ¶
Index ¶
- Constants
- func Broadcast(p *Packet)
- func NoInit(p *Packet)
- func ReplyGetIdentity(p *Packet)
- func ReplyGetState(p *Packet)
- func ReplyStateEmpty(p *Packet)
- func RunForever(p *Packet)
- type BridgeThingers
- type Bridger
- type Msg
- type MsgEventStatus
- type MsgIdentity
- type Packet
- func (p *Packet) Broadcast()
- func (p *Packet) Copy(dst *Packet)
- func (p *Packet) From() string
- func (p *Packet) IsThing() bool
- func (p *Packet) Marshal(msg interface{}) *Packet
- func (p *Packet) Reply()
- func (p *Packet) Send(dst string)
- func (p *Packet) Src() string
- func (p *Packet) String() string
- func (p *Packet) Unmarshal(msg interface{})
- type Subscribers
- type Thing
- type ThingAssets
- type ThingConfig
- type Thinger
Constants ¶
const ( // CmdInit is guaranteed to be the first message a new Thing will see. // Thing can optionally subscribe and handle CmdInit via Subscribers(), // to initialize Thing's state. // // CmdInit is not sent to Thing Prime. Thing Prime will get its // initial state with a GetState call to Thing. CmdInit = "_CmdInit" // CmdRun is Thing's main loop. All Things must subscribe and handle // CmdRun, via Subscribers(). CmdRun should run forever; it is an error // for CmdRun handler to exit. // // CmdRun is not sent to Thing Prime. Thing Prime does not have a main // loop. // // If the Thing is a bridge, CmdRun is also sent to the bridge bus on // startup of the bridge, via BridgeSubscribers(). In this case, CmdRun // is optional and doesn't need to run forever. CmdRun = "_CmdRun" // GetIdentity requests Thing's identity. Thing does not need to // subscribe to GetIdentity. Thing will internally respond with a // ReplyIdentity message. GetIdentity = "_GetIdentity" // Response to GetIdentity. ReplyIdentity message is coded as // MsgIdentity. ReplyIdentity = "_ReplyIdentity" // GetState requests Thing's state. Thing should respond with a // ReplyState message containing Thing's state. GetState = "_GetState" // Response to GetState. ReplyState message coding is Thing-specific. // // It is convenient to use Thing's type struct (the Thinger) as the // container for Thing's state. Just include a Msg member and export // any other state members (with an uppercase leading letter). Then // the whole type struct can be passed in p.Marshal() to form the // response. // // type thing struct { // Msg string // StateVar0 int // StateVar1 bool // // non-exported members // } // // func (t *thing) init(p *merle.Packet) { // t.StateVar0 = 42 // t.StateVar1 = true // } // // func (t *thing) getState(p *merle.Packet) { // t.Msg = merle.ReplyState // p.Marshal(t).Reply() // } // // Will send JSON message: // // { // "Msg": "_ReplyState", // "StateVar0": 42, // "StateVar1": true, // } ReplyState = "_ReplyState" // EventStatus message is an unsolicited notification that a child // Thing's connection status has changed. // // EventStatus message is coded as MsgEventStatus. EventStatus = "_EventStatus" )
System messages. System messages are prefixed with '_'.
Variables ¶
This section is empty.
Functions ¶
func Broadcast ¶
func Broadcast(p *Packet)
Subscriber helper function to broadcast Packet.
In this example, any Packets received with message Alert are broadcast to all other listeners:
return merle.Subscribers{ ... "Alert": merle.Broadcast, }
func NoInit ¶ added in v0.0.27
func NoInit(p *Packet)
Subscriber helper function to do nothing on CmdInit. Example:
return merle.Subscribers{ merle.CmdInit: merle.NoInit, ... }
func ReplyGetIdentity ¶ added in v0.0.24
func ReplyGetIdentity(p *Packet)
Subscriber helper function to GetIdentity. Example of chaining the EventStatus change notification to send a GetIdentity request:
return merle.Subscribers{ ... merle.EventStatus: merle.ReplyGetIdentity, merle.ReplyIdentity: t.identity, }
func ReplyGetState ¶ added in v0.0.24
func ReplyGetState(p *Packet)
Subscriber helper function to GetState
func ReplyStateEmpty ¶ added in v0.0.24
func ReplyStateEmpty(p *Packet)
Subscriber helper function to return empty state in response to GetState. Example:
return merle.Subscribers{ ... merle.GetState: merle.ReplyStateEmpty, }
func RunForever ¶
func RunForever(p *Packet)
Subscriber helper function to run forever. Only applicable for CmdRun.
return merle.Subscribers{ ... merle.CmdRun: merle.RunForever, }
Types ¶
type BridgeThingers ¶
BridgeThingers is a map of functions which can generate Thingers, keyed by a regular expression (re) of the form: id:model:name. The keys specify which Things can attach to the bridge.
type Bridger ¶
type Bridger interface { // Map of Thingers supported by Bridge. Map keyed by a regular // expression (re) of the form: id:model:name specifying which Things // can attach to the bridge. E.g.: // // return merle.BridgeThingers{ // ".*:relays:.*": func() merle.Thinger { return relays.NewRelays() }, // ".*:bmp180:.*": func() merle.Thinger { return bmp180.NewBmp180() }, // } // // In this example, a Thing with [id:model:name] = "01234:relays:foo" // would match the first entry. Another Thing with "8888:foo:bar" // would not match either entry and would not attach. BridgeThingers() BridgeThingers // List of subscribers on Bridge bus. All packets from all connected // Things (children) are forwarded to the Bridge bus and tested against // the BridgeSubscribers. BridgeSubscribers() Subscribers }
A Thing implementing the Bridger interface is a Bridge
type Msg ¶ added in v0.0.24
type Msg struct {
Msg string
}
All messages in Merle build on this basic struct. All messages have a member Msg which is the message type, a string that's unique within the Thing's message namespace.
System messages type Msg is prefixed with a "_". Regular Thing messages should not be prefixed with "_".
type MsgEventStatus ¶ added in v0.0.24
Event status change notification message. On child connect or disconnect, this notification is sent to:
1. If Thing Prime, send to all listeners (browsers) on Thing Prime.
2. If Bridge, send to mother bus and to bridge bus.
type MsgIdentity ¶
type MsgIdentity struct { Msg string Id string Model string Name string Online bool StartupTime time.Time }
Thing identification message return in ReplyIdentity
type Packet ¶
type Packet struct {
// contains filtered or unexported fields
}
A Packet is the basic unit of communication in Merle. Thing Subscribers() receive, process and optional forward Packets. A Packet contains a single message and the message is JSON-encoded.
func (*Packet) Broadcast ¶
func (p *Packet) Broadcast()
Broadcast the Packet to everyone else on the bus. Do not hold locks when calling Broadcast().
func (*Packet) IsThing ¶ added in v0.0.24
Test if this is the real Thing or Thing Prime.
If p.IsThing() is not true, then we're on Thing Prime and should not access device I/O and only update Thing's software state. If p.IsThing() is true, then this is the real Thing and we can access device I/O.
func (*Packet) Reply ¶
func (p *Packet) Reply()
Reply back to sender of Packet. Do not hold locks when calling Reply().
func (*Packet) Send ¶ added in v0.0.24
Send Packet to destination TODO: Use restrictions? Only to be called from bridge, or could be called TODO: from child to talk to another child, over a bridge?
type Subscribers ¶
Subscribers is a map of message subscribers, keyed by Msg type. On Packet receipt, the Packet Msg is used to lookup a subscriber. If a match, the subscriber handler is called to process the Packet.
Here's an example Subscribers() map:
func (t *thing) Subscribers() merle.Subscribers { return merle.Subscribers{ merle.CmdInit: t.init, merle.CmdRun: t.run, merle.GetState: t.getState, merle.EventStatus: nil, "SetPoint": t.setPoint, }
A subscriber handler is a function that takes a Packet pointer as it's only argument. An example handler for the "SetPoint" Msg above:
func (t *thing) setPoint(p *merle.Packet) { // do something with Packet p }
If the handler is nil, a Packet will be dropped silently.
If the key "default" exists, then the default handler is called for any non-matching Packets. Here's an example BridgeSuscribers() that silently drops all packets except CAN messages:
func (b *bridge) BridgeSubscribers() merle.Subscribers { return merle.Subscribers{ "CAN": merle.Broadcast, // broadcast CAN msgs to everyone "default": nil, // drop everything else silently } }
type Thing ¶
type Thing struct { // Thing's configuration Cfg ThingConfig // contains filtered or unexported fields }
Thing made from a Thinger.
type ThingAssets ¶
type ThingAssets struct { // Directory on file system for Thing's assets (html, css, js, etc) // This is an absolute or relative directory. If relative, it's // relative to the Thing's binary path. AssetsDir string // Path to Thing's HTML template file, relative to AssetsDir. HtmlTemplate string // HtmlTemplateText is text passed in lieu of a template file. // HtmlTemplateText takes priority over HtmlTemplate, if both are // present. HtmlTemplateText string }
type ThingConfig ¶
type ThingConfig struct { // ########## Thing configuration. // // [Optional] Thing's Id. Ids are unique within an application to // differentiate one Thing from another. Id is optional; if Id is not // given, a system-wide unique Id is assigned. Id string // Thing's Model. The default is "Thing". Model string // Thing's Name. The default is "Thingy". Name string // [Optional] system User. If a User is given, any browser views of // the Thing's UI will prompt for user/passwd. HTTP Basic // Authentication is used and the user/passwd given must match the // system creditials for the user. If no user is given, HTTP Basic // Authentication is skipped; anyone can view the UI. The default is // "" (skipped). User string // [Optional] If PortPublic is non-zero, an HTTP web server is started // on port PortPublic. PortPublic is typically set to 80. The HTTP // web server runs Thing's UI. The default is 0 (no web server). PortPublic uint // [Optional] If PortPublicTLS is non-zero, an HTTPS web server is // started on port PortPublicTLS. PortPublicTLS is typically set to // 443. The HTTPS web server will self-certify using a certificate // from Let's Encrypt. The public HTTPS server will securely run the // Thing's UI. If PortPublicTLS is given, PortPublic must be given. // The default is 0 (no web server). PortPublicTLS uint // [Optional] If PortPrivate is non-zero, a private HTTP server is // started on port PortPrivate. This HTTP server does not server up // the Thing's UI but rather connects to Thing's Mother using a // websocket over HTTP. The default is 0 (no web server). PortPrivate uint // [Optional] Run as Thing-prime. The default is false. IsPrime bool // MaxConnection is maximum number of inbound connections to a Thing. // Inbound connections are WebSockets from web browsers or WebSockets // from Thing Prime. The default is 30. With the default, the 31st // (and higher) concurrent WebSocket connection attempt will block, // waiting for one of the first 30 WebSocket sessions to terminate. MaxConnections uint // Logging enable LoggingEnabled bool // ########## Mother configuration. // // This section describes a Thing's mother. Every Thing has a mother. A // mother is also a Thing. We can build a hierarchy of Things, with a Thing // having a mother, a grandmother, a great grandmother, etc. // // Mother's Host address. This the IP address or Domain Name of the // host running mother. Host address can be on the local network or across // the internet. MotherHost string // User on host with SSH access into host. Host should be configured // with user's public key so SSH access is password-less. MotherUser string // Port on Host for Mother's private HTTP server MotherPortPrivate uint // ########## Bridge configuration. // // A Thing implementing the Bridger interface will use this config for // bridge-specific configuration. // // Beginning bridge port number. The bridge will listen for Thing // (child) connections on the port range [BeginPort-EndPort]. // // The bridge port range must be within the system's // ip_local_reserved_ports. // // Set a range using: // // sudo sysctl -w net.ipv4.ip_local_reserved_ports="6000-6100" // // Or, to persist setting on next boot, add to /etc/sysctl.conf: // // net.ipv4.ip_local_reserved_ports = 6000-6100 // // And then run sudo sysctl -p // BridgePortBegin uint // Ending bridge port number BridgePortEnd uint }
type Thinger ¶
type Thinger interface { // Map of Thing's subscribers, keyed by message. On Packet receipt, a // subscriber is looked up by Packet message. If there is a match, the // subscriber callback is called. If no subscribers match the received // message, the "default" subscriber matches. If still no matches, the // Packet is not handled. If the callback is nil, the Packet is // (silently) dropped. Here is an example of a subscriber map: // // func (t *thing) Subscribers() merle.Subscribers { // return merle.Subscribers{ // merle.CmdRun: t.run, // merle.GetState: t.getState, // merle.ReplyState: t.saveState, // "SpamUpdate": t.update, // "SpamTimer": nil, // silent drop // } // } // Subscribers() Subscribers // Thing's web server assets. Assets() *ThingAssets }
All Things implement the Thinger interface.
To be a Thinger, the Thing must implement the two methods, Subscribers() and Assets():
type thing struct {} func (t *thing) Subscribers() merle.Subscribers { ... } func (t *thing) Assets() *merle.ThingAssets { ... }