UNPKG

thingmodel

Version:

Synchronize data and models across multiple devices

340 lines (270 loc) 9.54 kB
module ThingModel.Proto { export class ToProtobuf { // Current transaction object // it will be filled step by step private _transaction: Transaction; // String declaration dictionary public StringDeclarations: { [value: string]: number }; public StringDeclarationsCpt: number; // String to declares private _stringToDeclare: { [value: string]: boolean }; private _thingsState: { [id: number]: Thing }; private _propertiesState: { [key: string]: Property }; constructor() { this.StringDeclarations = {}; this.StringDeclarationsCpt = 0; this._stringToDeclare = {}; this._thingsState = {}; this._propertiesState = {}; } private StringToKey(value: string): number { if (!value) { return 0; } var key = this.StringDeclarations[value]; if (key) { return key; } key = ++this.StringDeclarationsCpt; this.StringDeclarations[value] = key; var stringDeclaration = new ProtoTools.Builder.StringDeclaration(); stringDeclaration.setKey(key); stringDeclaration.setValue(value); this._transaction.string_declarations.push(stringDeclaration); return key; } public Convert(publish: ThingModel.Thing[], deletions: ThingModel.Thing[], declarations: ThingModel.ThingType[], senderID: string): Transaction { this._transaction = new ProtoTools.Builder.Transaction(); this._transaction.setStringSenderId(this.StringToKey(senderID)); _.each(publish, (thing) => { this.ConvertThing(thing); }); this.ConvertDeleteList(deletions); this.ConvertDeclarationList(declarations); return this._transaction; } public ConvertTransaction(transaction: Transaction): ArrayBuffer { return transaction.toArrayBuffer(); } private ConvertThing(thing: ThingModel.Thing): void { var change = false; var thingID = this.StringToKey(thing.ID); var publication = new ProtoTools.Builder.Thing(); publication.setStringId(thingID); publication.setStringTypeName(thing.Type != null ? this.StringToKey(thing.Type.Name) : 0); publication.setConnectionsChange(false); var previousThing = this._thingsState[thingID]; if (previousThing == null || previousThing.getStringTypeName() != publication.getStringTypeName()) { change = true; } var connectedThingsCache: ThingModel.Thing[] = null; if ((previousThing == null && thing.ConnectedThingsCount > 0) || (previousThing != null && previousThing.getConnections().length != thing.ConnectedThingsCount)) { publication.setConnectionsChange(true); } else { connectedThingsCache = thing.ConnectedThings; _.any(connectedThingsCache, (connectedThing: ThingModel.Thing) => { var connectionKey = this.StringToKey(connectedThing.ID); if (previousThing == null || !_.contains(previousThing.getConnections(), connectionKey)) { publication.setConnectionsChange(true); return true; } return false; }); } if (publication.getConnectionsChange()) { change = true; if (!connectedThingsCache) { connectedThingsCache = thing.ConnectedThings; } _.each(connectedThingsCache, (connectedThing: ThingModel.Thing) => { var connectionKey = this.StringToKey(connectedThing.ID); publication.connections.push(connectionKey); }); } _.each(thing.Properties, (property: ThingModel.Property) => { var propertyId = this.StringToKey(property.Key); var proto = new ProtoTools.Builder.Property(); proto.setStringKey(propertyId); switch (property.Type) { case Type.LocationLatLng: case Type.LocationPoint: case Type.LocationEquatorial: this.ConvertLocationProperty(property, proto); break; case Type.String: this.ConvertStringProperty( <ThingModel.Property.String> property, proto); break; case Type.Double: proto.setType(Property.Type.DOUBLE); proto.setDoubleValue( (<ThingModel.Property.Double> property).Value); break; case Type.Int: proto.setType(Property.Type.INT); proto.setIntValue( (<ThingModel.Property.Int> property).Value); break; case Type.Boolean: proto.setType(Property.Type.BOOLEAN); proto.setBooleanValue( (<ThingModel.Property.Boolean> property).Value); break; case Type.DateTime: proto.setType(Property.Type.DATETIME); proto.setDatetimeValue( (<ThingModel.Property.DateTime> property).Value.getTime()); break; } // We are in a JavaScript world :-) var propertyStateKey = thingID + ":" + propertyId; if (previousThing != null) { var previousProto = this._propertiesState[propertyStateKey]; // Terrible part :) if (previousProto != null && previousProto.getType() == proto.getType() && previousProto.getBooleanValue() == proto.getBooleanValue() && previousProto.getDatetimeValue() == proto.getDatetimeValue() && previousProto.getDoubleValue() == proto.getDoubleValue() && previousProto.getIntValue() == proto.getIntValue()) { var previousLoc = previousProto.getLocationValue(); var loc = proto.getLocationValue(); if ((previousLoc == null && loc == null) || (previousLoc != null && loc != null && previousLoc.x == loc.x && previousLoc.y == loc.y && previousLoc.z == loc.z && previousLoc.z_null == loc.z_null && previousLoc.getStringSystem() == loc.getStringSystem())) { var previousStr = previousProto.getStringValue(); var str = proto.getStringValue(); if ((previousStr == null && str == null) || (previousStr != null && str != null && ((previousStr.getStringValue() == str.getStringValue() && previousStr.getValue() == str.getValue()) || (property.ValueToString() == previousStr.getValue())))) { return; } } } } change = true; publication.properties.push(proto); this._propertiesState[propertyStateKey] = proto; }); if (change) { this._transaction.things_publish_list.push(publication); this._thingsState[thingID] = publication; } } private ConvertDeleteList(list: ThingModel.Thing[]): void { _.each(list, (thing: ThingModel.Thing) => { var key = this.StringToKey(thing.ID); this._transaction.things_remove_list.push(key); this.ManageThingSuppression(key); }); } private ConvertDeclarationList(list: ThingModel.ThingType[]): void { _.each(list, (thingType: ThingModel.ThingType) => { var declaration = new ProtoTools.Builder.ThingType(); declaration.setStringName(this.StringToKey(thingType.Name)); declaration.setStringDescription(this.StringToKey(thingType.Description)); _.each(thingType.Properties, (propertyType: ThingModel.PropertyType) => { var prop = new ProtoTools.Builder.PropertyType(); prop.setStringKey(this.StringToKey(propertyType.Key)); prop.setStringName(this.StringToKey(propertyType.Name)); prop.setStringDescription(this.StringToKey(propertyType.Description)); prop.setRequired(propertyType.Required); switch (propertyType.Type) { case Type.LocationLatLng: case Type.LocationEquatorial: case Type.LocationPoint: case Type.Location: prop.setType(PropertyType.Type.LOCATION); break; case Type.String: prop.setType(PropertyType.Type.STRING); break; case Type.Double: prop.setType(PropertyType.Type.DOUBLE); break; case Type.Int: prop.setType(PropertyType.Type.INT); break; case Type.Boolean: prop.setType(PropertyType.Type.BOOLEAN); break; case Type.DateTime: prop.setType(PropertyType.Type.DATETIME); break; } declaration.properties.push(prop); }); this._transaction.thingtypes_declaration_list.push(declaration); }); } private ConvertLocationProperty(property: ThingModel.Property, proto: Property) { var value = (<any>property).Value; switch (value.type) { case "latlng": proto.setType(Property.Type.LOCATION_LATLNG); break; case "equatorial": proto.setType(Property.Type.LOCATION_EQUATORIAL); break; case "point": default: proto.setType(Property.Type.LOCATION_POINT); break; } var loc = new ProtoTools.Builder.Property.Location(); loc.setX(value.X); loc.setY(value.Y); loc.setStringSystem(this.StringToKey(value.System)); loc.setZNull(value.Z == null); if (value.Z != null) { loc.setZ(value.Z); } proto.setLocationValue(loc); } private ConvertStringProperty(property: ThingModel.Property.String, proto: Property) { var value = property.Value; proto.setType(Property.Type.STRING); var st = new ProtoTools.Builder.Property.String(); if (value && this._stringToDeclare[value]) { st.setStringValue(this.StringToKey(value)); } else { st.setValue(value); // Use a string decleration next time this._stringToDeclare[value] = true; } proto.setStringValue(st); } private ManageThingSuppression(thingId: number): void { delete this._thingsState[thingId]; var stringId = thingId + ":"; _.each(this._propertiesState, (value, key) => { if (key.indexOf(stringId) === 0) { delete this._propertiesState[key]; } }); } public ApplyThingsSuppressions(things: ThingModel.Thing[]): void { _.each(things, (thing: ThingModel.Thing) => { var key = this.StringDeclarations[thing.ID]; if (key) { this.ManageThingSuppression(key); } }); } } }