def set_geofence(asset_id, geofence, inclusivity): """ Set a geofence for an asset. First, removes all current geofences attached to the asset. (This is not necessary, but this app limits assets to only one fence for simplicity's sake.) Then the geofence passed as a parameter is applied to the asset. If the geofence is empty, clears the existing geofence. A random type name is associated with each fence to show functionality. Raises RuntimeError we get an error or unexpected data from mCore. Raises KeyError if the asset does not exist. """ geofence = [(float(x), float(y)) for x, y in list(geofence)] a_response = fms_client.get_asset(asset_id=asset_id) if 'assetVO' not in a_response: raise KeyError('Asset does not exist') if 'geofenceVOs' in a_response.assetVO: for fence in a_response.assetVO.geofenceVOs: fms_client.delete_geofence(fence.geofenceId) if not geofence: # No geofence to add. Done return geo_id = {'inclusive': 1, 'exclusive': 2, } geofence_type = str(random.randint(0, 100)) request = webapp2.get_request() org_id = request.registry['session'].get('organization_id') fms_client.create_geofenceType(1, geofence_type) g_response = fms_client.create_geofence(geofence, "asset" + str(asset_id) + "_fence", geo_id[inclusivity], geofence_type, org_id) log.info(g_response) if 'entityId' in g_response: resp = fms_client.apply_geofence(asset_id, g_response.entityId) return {'code': resp.code, 'message': resp.message} else: raise RuntimeError(str(g_response))
def get_diagnostics(org_id, asset_id): ''' Returns a dictionary of the obd data for one asset. ''' asset_response = fms_client.get_asset(asset_id) device_serial = asset_response.assetVO.deviceSerial response = fms_client.get_diagnostic_data(device_serial) if not 'diagnosticDataVOs' in response: return elif len(response.diagnosticDataVOs) == 0: diagnostics = {asset_id: [{ 'Absolute Throttle Position': None, 'AirIntake Temperature': None, 'Cruise Control Status': None, 'Cumulative Fuel Used': None, 'Date Created': None, 'Device Serial': device_serial, 'Diagnostic Data Id': None, 'Dtc Errors': None, 'Dtc Value': None, 'Engine Coolant Temperature': None, 'Engine Rpm': None, 'Ignition State': None, 'Instantaneous Fuel Rate': None, 'Load Percentage': None, 'Long Term Fuel Trim': None, 'Mass Air Flow': None, 'Odometer Reading': None, 'Pto Information': None, 'Record Type': None, 'Short Term Fuel Trim': None, 'Speed': None }]} else: # Diagnostics is a data stucture of the form: # {asset_id: [{key1: value1, key2: value2, ... } # {key1: value1, key2: value2, ... } # ... # ] # } diagnostics = { asset_id: [ {make_readable(record[0]): record[1] for record in OBDdata} for OBDdata in response.diagnosticDataVOs ] } return diagnostics
def create_current_config(org_id, asset_id): ''' Creates a consumable representation of an asset's current configuration. Merges the current configuration of a device with the list of all possible features to be configured for a device of its type. It then transorms this information into a structure useable by the templating engine. ''' #Get the asset's configuration and its device type's default configuration asset = fms_client.get_asset(asset_id) serial = asset.assetVO.deviceSerial device = device_client.get_device(org_id, serial=serial) type_ = device.deviceVO.deviceTypeVO.deviceTypeId deviceResponse = device_client.get_device_configuration(serial) defaultResponse = device_client.get_device_type_configuration(type_) #Combine the current configuration with the default one combined_dict = {} _update_combined( combined_dict, defaultResponse.deviceTypeConfigurationVO.featureVOs) if deviceResponse.deviceConfigurationVO is not None: # a device's config only exists if it's been configured previously _update_combined( combined_dict, deviceResponse.deviceConfigurationVO.featureVOs) #Create a data structure suitable for the template. Note that this # is specific to these applications and not especially important device_config_list = [] for feature_id, feature_name in combined_dict: feat = {'id': feature_id, 'name': feature_name, 'attrs': sorted(combined_dict[(feature_id, feature_name)].values(), key=lambda attr: attr.attrName) # sort by name } device_config_list.append(feat) return sorted(device_config_list, key=lambda element: element['name'])
def get_last_location(asset_id): """ Get the last location for an asset. Returns a dict of lat/long and the last time updated. If the asset has no last location, returns None. If the asset doesn't exist on the account, raises KeyError. """ response = fms_client.get_asset(asset_id=asset_id) if response.assetVO is None: raise KeyError("Asset Does Not Exist!") asset = response.assetVO if asset.assetLastLocationVO is None: return None else: last_location = asset.assetLastLocationVO return {'latitude': last_location.latitude, 'longitude': last_location.longitude, 'updated': last_location.dtUpdated}
def get_geofence(asset_id): """Get the currently set geofence for an asset. Will return one of the geofences if multiple are set. Returns a list of [latitude, longitude] for the set geofence, if set. Returns None if no geofence is set Raises KeyError if the asset does not exist. """ response = fms_client.get_asset(asset_id=asset_id) if response.assetVO is None: raise KeyError("Asset Does Not Exist!") if 'geofenceVOs' not in response.assetVO: # no geofence is set return None # Note that without str() points_string would be a suds 'Text' object points_string = str(response.assetVO.geofenceVOs[0].geofenceGeometry) # from http://bit.ly/KOB9JI (stackoverflow comment) # Match all numbers in the POLYGON((long lat,long lat, ...., long lat)) # string nums = map(float, re.findall( r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?", points_string)) # Assume valid format parsed = [(latitude, longitude) for longitude, latitude in zip(nums[::2], nums[1::2])] if not parsed: return None else: return parsed
def send_configuration(asset_id, config_data): ''' Overwrites the asset's configuration with the one given on the page. The page's form data is parsed and sanitized, then inserted in the appropriate places in the default configuration. This function sends every feature, but notice that this isn't necessary. If only one option is changed, only that feature needs to be sent back to the device. ''' #Get the device type's default configuration asset = fms_client.get_asset(asset_id) serial = asset.assetVO.deviceSerial device = device_client.get_device(asset.assetVO.organizationId, serial=serial) type_ = device.deviceVO.deviceTypeVO.deviceTypeId default = device_client.get_device_type_configuration(type_) # Set up default configuration details for quick access features = default.deviceTypeConfigurationVO.featureVOs feat_names = {f.featureId: f.featureName for f in features} default_attrs = {feature.featureId: {attribute.attrId: attribute for attribute in feature.attributeVOs} for feature in features} # Set up form data for insertion into default_attrs. Note that this is # specific to this application. new_configs = {} for (feat_attr, attr_value) in config_data: # This is where the strange form names come in handy feat_id, attr_id = [int(elem) for elem in feat_attr.split('_')] new_configs.setdefault(feat_id, {}) new_configs[feat_id][attr_id] = attr_value #Sanitize form data and insert back into the default configurations changed_features = [] for feat_id in new_configs: changed_features.append(feat_id) for attr_id in new_configs[feat_id]: attr_value = new_configs[feat_id][attr_id] if attr_value == 'on': attr_value = '1' if attr_value == '': attr_value = None default_attrs[feat_id][attr_id].attrValue = attr_value #Revert default_configs to a nested list structure configuration = [] for feat_id in changed_features: featureVO = device_client.create_feature(feat_id, feat_names[feat_id]) featureVO.attributeVOs = default_attrs[feat_id].values() configuration.append(featureVO) #Note that `configuration' is now in the correct data structure for sending # to MAP and the device. That is, configuration is a list of features, each # of which is a list of attributes. Each attribute has an attrName and an # attrValue field, as well as some secretarial fields like min, max, and ID response = device_client.update_configuration(serial, configuration) return {'code': response.code, 'message': response.message, }