fxbox / deprecated-taxonomy Goto Github PK
View Code? Open in Web Editor NEWThis repository has moved.
Home Page: https://github.com/fxbox/foxbox/
License: Mozilla Public License 2.0
This repository has moved.
Home Page: https://github.com/fxbox/foxbox/
License: Mozilla Public License 2.0
Currently drop functions are not called. This is likely because we don't stop the several threads we're running. One way to do this is to make the SHUTDOWN_FLAG public so that every threads can access it. But it wouldn't be nice for adapters that are in separate repositories.
Another way is to use a "stop" method on adapters, that would be called at the right time.
The openzwave adapter badly needs this.
Live example:
AdapterManager
calls Adapter::send_values
to process setter request;Adapter::send_values
calls AdapterManager.get_services
to figure out what service this setter belongs to.Now that we have some experience building an Adapter for a CRUD, let's see what we can improve.
Ping @fabricedesre, @mcav, @dhylands, @azasypkin .
Following up on our discussion about FanOn, LightOn, etc.
I think it might be useful to be able to get at the FanOn/LightOn On/Off stuff as strings rather than as rust enums.
It sounds like we're going to wind up having many aliases for the same thing, which from the adapters point of view are all 100% identical. This means having a bunch of duplicated code in the adapter.
I'd rather have a file, or something which lists all of the strings which are actually aliases for one another and write the code once, and just append new aliases as they become available.
There isn't any place in my adapters where the enums are actually used in any kind of enum-meaningful way. The adapter basically has to translate each of the things which means on to true and off to false (or perhaps the inverse), so that it can send true/false to the device.
Also, for the zwave adapter, it will need to translate in both directions, since it will need to store some type of persistent mapping. For example, zwave knows that say Node 2 is a BinarySwitch. If the user has assigned that Node 2 controls a fan, and wishes to use FanOn, FanOff commands, then we need to persist that information someplace so that we can present Node 2 as a Fan in the getters & setters. So we're going to wind up having to persist the fact that its a Fan with FanOn/FanOff using strings as well, as well as adding getters and setters which allow the kind/type to changed.
Currently, we represent tags as String
. For performance, safety and memory usage, we should rather represent them using Id
.
For this purpose:
1/ Define in devices.rs
an empty type struct TagId;
2/ Find all occurrences of tags as String
and replace them with Id<TagId>
.
In rust target
should be in .gitignore. Nothing in it should be checked in.
There is some text in http://fxbox.github.io/adapters/doc/transformable_channels/mpsc/trait.ExtSender.html but the text is not complete and does not seem accurate.
There will probably be very few distinct mimetypes in the system, and we don't want to confuse a mimetype string with something else.
So, in values.rs
, in the definition of Binary
, we should use Id<MimetypeId>
instead of String
for the mimetype. This way, once issue #11 has landed, we'll benefit automatically from string caching.
Does this mean I have to create a custom kind to store strings?
For example, I need to add getters and setters to the camera to get/set the camera_name, the username and password, and we should probably have a location as well.
When calling register_watch we likely need to modify some internal structures. For this "&mut self" is necessary, or we'll need to use a Cell which is very inconvenient -- and could be dangerous.
Services have a property_bag
, which is a HashMap<String, String>
, immutable and provided during creation, that can contain data useful for the developer. We should have the same for channels.
Right now, requests are hard-coded structs. We should give them a proper API to make them more future-proof.
Right now the clock service is serialized as:
{
"adapter": "[email protected]",
"getters": {
"getter:[email protected]": {
"adapter": "[email protected]",
"id": "getter:[email protected]",
"kind": "CurrentTimeOfDay",
"mechanism": "getter",
"service": "service:[email protected]",
"tags": []
},
"getter:[email protected]": {
"adapter": "[email protected]",
"id": "getter:[email protected]",
"kind": "CurrentTime",
"mechanism": "getter",
"service": "service:[email protected]",
"tags": []
}
},
"id": "service:[email protected]",
"properties": {
"model": "Mozilla clock v1"
},
"setters": {},
"tags": []
}
I'd like getters to be an array to not repeat the getter id both as a property name and as an id value.
An examples where the setter returns a value, would be when you issue a take_snapshot command.
This will create an image, and perhaps a getter. If it creates a getter, it would be nice to know the id of the getter which was just created (the id that corresponds to the snapshot I just took).
I think that this will be important for retrieving historical data. i.e. we record someplace that we took a picture at this time with this ID so that it can be retrieved later.
Many Strings will appear several times (e.g. tags). To avoid copying them, we should use a mechanism of atoms, that makes sure we only have one instance of the String in memory at any given time.
It would be useful to have human readable labels that are distinct from the ID.
We need to fix this. I wonder if there's a tool that can help me find out what's pointing there.
Full log: https://travis-ci.org/fxbox/taxonomy/builds/119883198
Spinoff from an idea by @mcav in #53.
We could rewrite the AdapterManager
's channel API as follows:
/// A channel, which may have read/write capabilities.
pub struct Channel {
id: Id<Channel>,
kind: ChannelKind,
service: Id<ServiceId>,
pub properties: PropertyBag,
pub fetch: Option<Box< Fn(FetchArgs) -> Result<Value, Error> + Sync + Send>>,
pub send: Option<Box< Fn(SendArgs) -> Result<(), Error> + Sync + Send>>,
pub watch: Option<Box< Fn(WatchArgs) -> Result<WatchGuard, Error> + Sync + Send>>,
// Possibly more in the future.
}
impl Channel {
/// Create an empty channel.
pub fn new(id: &Id<Channel>, service: &Id<ServiceId>, kind: ChannelKind) -> Self;
}
impl AdapterManger {
pub fn add_channel(channel: Channel) -> Result<(), Error>
}
Id<Getter>
/Id<Setter>
, actual devices, actual implementation.Channel
much like this to the REST API, and it should often be easier to use than our current separate getters/setters.fetch
(respectively send
, watch
) requests by Adapter and send them together to minimize communications. I don't see how to do this with this design.Problem: The centralized enums of { ChannelType, Type, and Value } are tedious to extend, arbitrarily defined, and tend to be very adapter-specific.
Proposal: Rather than defining ChannelType, Type, and Value in centralized enums, we should pass around encoded values, allowing Adapters to decide how to decode that data, something like this:
struct EncodedValue {
content_type: String, // e.g. “temperature+json”
data: Vec<u8>
}
Adapters will decide how to decode EncodedValues themselves. This:
image/*
) rather than just one.Standards already describe a time-tested way of exchanging typed data and defining content types. (While we could certainly go full-throttle with application/vnd.mozilla-temperature+json
for things like “temperature” that haven’t been standardized, a simple temperature+json
format would be plenty specific.)
Ultimately, we just want a Content-Type to allow the consumer to go from a binary representation to the type they desire. I suggest MIME Types because adapters can take advantage of standardized types (cameras with image/jpeg
) and this pushes us toward a web-compatible direction.
I still see a role for taxonomy-like types with this approach. We can define types that need to be standardized and allow them to implement serialization to and from EncodedValue
objects. This allows us to retain the existing level of type-safety some people prefer, without restricting authors to defining new types in a rigid global enum.
While there are clear benefits to enforcing types, I think we need to take a step back from this full-on, define-the-world-rigidly approach. I think this proposal would greatly simplify both the Taxonomy and FoxBox code, leaving us in a better position for growth and stability. If you disagree, I'd like to better understand the merits of the current method as compared to this proposal.
temperature+json => { “C”: 42 }
boolean => 0 | 1
thinkerbell-rule+json => { ... }
color+rgba => 255 255 255 0
color+json => { r: 0, g: 0, b: 0, a: 255 }
image/jpeg
null => (no data)
Type Id
is defined in util.rs
.
We don't actually use that trait, it's just an annoyance.
It probably makes sense for a "device" to allow a min/max to be specified on values.
Branch decentralized
contains a new version of Taxonomy, based on initial brainstorming between @mcav and myself. It now passes its main tests, but a number of doctests have not been ported to the new API yet.
Once work on this repo is done, I'd like to work on
Before we can do that, however, we need to finish the work on this repo. @mcav, I don't know if you have some spare cycles at the moment, but I could use the help, both on this repo and on the followup work. I'm leaving on PTO for one week, so my ideal scenario would be one in which you have finished the work on this repo during the time and we can parallelize the rest when I return. If you prefer waiting until I return, I can understand, though.
At the moment, most of the variants of ServiceKind
are (de)serialized as key: []
, with the exception of Extension
. We should simply (de)serialize them as a string, with the exception of Extension
being (de)serialized as an object.
I came up with some examples where having a getter take parameters would be useful:
1 - Retrieving an image with some type of conversion specifier (i.e. format jpeg/png, scaling factor)
2 - Retrieving historical sensor data and specifying a date/time range that you're interested in
I keep losing track of whether we're talking about channels or channels.
What are better options? The classical Endpoint, perhaps?
For the moment, we handle Binary as a Vec<u8>
passed through JSON. That's pretty ugly, so we should find a better way to store Binaries.
fetch
handles that nicely, but I haven't checked.Cc-ing people who had to deal with Binary: @dhylands , @azasypkin .
This data is exposed by ZWave and may be useful for the UX.
Cc @julienw .
Managing watchers requires significant portions of code in adapters that is beginning to look like massive boilerplate. Instead, managing watchers, range checking and notification should happen centrally in the adapter manager.
I suggest to have adapters declare on getter registration whether or not a getter support updates. One way to go about this might be to modify the return type to pass an optional mpsc channel that the adapter uses to inform the adapter manager of every update to the getter's value:
fn add_getter(&self, setter: Channel<Getter>) -> Result<Option<Sender<T>>, Error>;
That way we can completely remove register_watch() from the Adapter: Send trait and can have a well-tested central implementation of all the watcher logic.
Use case: in zwave we don't always have all the information at startup, especially for new nodes. So we get a message NodeNaming only after some time ("some time" being quite different depending on the devices) when this information is ready.
A few days ago, a problem on Taxonomy (on a personal branch, moreover), caused breakage on Foxbox for everyone.
To solve this issue – and get rid of all the problems we have with pinning Taxonomy – we need a script that gets Travis to rebuild Foxbox with a version of Taxonomy before we merge that version. This would considerably simplify the work of updating Taxonomy.
@JohanLorenzo Could you handle that?
At the moment, we have a variant Value::Bool
, which is used for determining whether a door is opened, a device is on, etc. That's ambiguous.
Rather, we should have:
Value::Opening(Opening)
with enum Opening { Open, Closed }
;Value::OnOff(OnOff)
, with enum OnOff { On, Off }
;Value::ExtBool
, with the same kind of structure as Value::ExtNumeric
.We need to chose another term for this concept. Naming is hard... Maybe Node
(vs. leaf
) ?
The name Unit
is quite unclear, so let's replace it with something easier to understand.
We should probably have OvenTemperature
, HeaterTemperature
, SensorTemperature
...
I'd like to write a Thinkerbell rule (or, really, any send
operation) that turns off all lights in the living room. Let's assume that lights are defined by channels with kinds OnOff
and Luminosity
.
How can I do it?
Off
to all channels that support OnOff
in the living room can turn off devices that are not lights, so this is not what we want.OnOff
and Luminosity
, then taking the OnOff
channels of such services gives us that list, but we need to rely upon the Id
of services, which means that the rule will break when we replace the device.We need better selectors.
This will simplify a few things, and possibly the serialization format.
We have intermittent test errors. We need to fix them.
For the moment, we rely on Serde's #[derive(Serialize, Deserialize)]
. This is very convenient, but:
Piece-by-piece and bottom-up, we should:
Serialize
, Deserialize
manually;It's unclear whether we really need a hierarchy of hubs.
One of the main use cases of tags is to let users label devices by their usecase and/or their location (e.g. "location: bedroom" or "usage: intrusion detector"). In most cases, users will probably want to label per Service
, rather than per Channel
.
To handle this, we need to update GetterSelector
and SetterSelector
to:
selector_tags: HashSet<Id<TagId>>
;pub fn with_selector_tags(self, HashSet<Id<TagId>>) -> Self
;pub fn matches(&self, &Channel<_>) -> bool
with a pub fn matches(&self, &Channel<_>, &Service) -> bool
that implements the actual selection.A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.