dt_factory.py 16.8 KB
Newer Older
Jiahang Chen's avatar
Jiahang Chen committed
1
""" This module implements a factory for managing and creating digital twins."""
C. Albrecht's avatar
WIP  
C. Albrecht committed
2
3

from ml.app_logger import APP_LOGGER
Jiahang Chen's avatar
Jiahang Chen committed
4
from ml.tools import remove_namespace
Jiahang Chen's avatar
Jiahang Chen committed
5

Jiahang Chen's avatar
Jiahang Chen committed
6
from ml.thing import Thing
Jiahang Chen's avatar
Jiahang Chen committed
7
from ml.ml40.roles.servives.service import Service
Jiahang Chen's avatar
Jiahang Chen committed
8
9
10
11
from ml.ml40.roles.hmis.app import App
from ml.ml40.roles.hmis.dashboard import Dashboard
from ml.ml40.roles.hmis.machine_ui import MachineUI
from ml.ml40.roles.hmis.hmd import HMD
Jiahang Chen's avatar
Jiahang Chen committed
12
from ml.ml40.roles.hmis.hmi import HMI
Jiahang Chen's avatar
Jiahang Chen committed
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from ml.ml40.roles.dts.handheld_devices.handheld_device import HandheldDevice
from ml.ml40.roles.dts.machines.machine import Machine
from ml.ml40.roles.dts.parts.crane import Crane
from ml.ml40.roles.dts.parts.part import Part
from ml.ml40.roles.dts.parts.engine import Engine
from ml.ml40.roles.dts.parts.scale import Scale
from ml.ml40.roles.dts.parts.tank import Tank
from ml.ml40.roles.dts.persons.machine_operator import MachineOperator
from ml.ml40.roles.dts.persons.person import Person
from ml.ml40.roles.dts.sensors.sensor import Sensor
from ml.ml40.roles.dts.sensors.air_sensor import AirSensor
from ml.ml40.roles.dts.sensors.soil_sensor import SoilSensor
from ml.ml40.roles.dts.sites.site import Site
from ml.ml40.roles.dts.ways.way import Way

from ml.fml40.roles.dts.handheld_devices.brushcutter import Brushcutter
from ml.fml40.roles.dts.handheld_devices.chainsaw import Chainsaw
from ml.fml40.roles.dts.machines.forest_machine import ForestMachine
from ml.fml40.roles.dts.machines.forwarder import Forwarder
Jiahang Chen's avatar
Jiahang Chen committed
32
33
from ml.fml40.roles.dts.machines.harvester import Harvester
from ml.fml40.roles.dts.machines.log_truck import LogTruck
Jiahang Chen's avatar
Jiahang Chen committed
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from ml.fml40.roles.dts.machines.mini_tractor import MiniTractor
from ml.fml40.roles.dts.machines.skidder import Skidder
from ml.fml40.roles.dts.machines.wheel_loader import WheelLoader
from ml.fml40.roles.dts.parts.grabber import Grabber
from ml.fml40.roles.dts.parts.harvesting_head import HarvestingHead
from ml.fml40.roles.dts.parts.log_loading_area import LogLoadingArea
from ml.fml40.roles.dts.parts.saw import Saw
from ml.fml40.roles.dts.parts.winch import Winch
from ml.fml40.roles.dts.persons.forest_owner import ForestOwner
from ml.fml40.roles.dts.persons.forest_worker import ForestWorker
from ml.fml40.roles.dts.persons.mini_tractor_operator import MiniTractorOperator
from ml.fml40.roles.dts.persons.skidder_operator import SkidderOperator
from ml.fml40.roles.dts.sensors.vitality_sensor import VilalitySensor
from ml.fml40.roles.dts.sites.forest_enterprise import ForestEnterprise
from ml.fml40.roles.dts.sites.hauler import Hauler
from ml.fml40.roles.dts.sites.mill.mill import Mill
from ml.fml40.roles.dts.sites.mill.papermill import Papermill
from ml.fml40.roles.dts.sites.mill.sawmill import Sawmill
Jiahang Chen's avatar
Jiahang Chen committed
52
53
54
55
56
57
from ml.fml40.roles.dts.woods.wood import Wood
from ml.fml40.roles.dts.woods.stem_segment import StemSegment
from ml.fml40.roles.dts.woods.wood_pile import WoodPile
from ml.fml40.roles.dts.forest.forest import Forest
from ml.fml40.roles.dts.forest.forest_segment import ForestSegment
from ml.fml40.roles.dts.forest.tree import Tree
Jiahang Chen's avatar
Jiahang Chen committed
58

Jiahang Chen's avatar
Jiahang Chen committed
59
60
from ml.ml40.features.properties.associations.association import Association
from ml.ml40.features.properties.associations.composite import Composite
Jiahang Chen's avatar
Jiahang Chen committed
61
from ml.ml40.features.properties.property import Property
Jiahang Chen's avatar
Jiahang Chen committed
62
from ml.ml40.features.properties.associations.shared import Shared
Jiahang Chen's avatar
Jiahang Chen committed
63
64
65
66
67
from ml.ml40.features.properties.values.address import Address
from ml.ml40.features.properties.values.dimensions import Dimensions
from ml.ml40.features.properties.values.generic_property import GenericProperty
from ml.ml40.features.properties.values.liquid_filling_level import LiquidFillingLevel
from ml.ml40.features.properties.values.location import Location
C. Albrecht's avatar
WIP  
C. Albrecht committed
68
from ml.ml40.features.properties.values.moisture import Moisture
Jiahang Chen's avatar
Jiahang Chen committed
69
70
71
72
73
74
75
76
77
78
79
from ml.ml40.features.properties.values.personal_name import PersonalName
from ml.ml40.features.properties.values.rotational_speed import RotationalSpeed
from ml.ml40.features.properties.values.route import Route
from ml.ml40.features.properties.values.temperature import Temperature
from ml.ml40.features.properties.values.time_slot import TimeSlot
from ml.ml40.features.properties.values.weight import Weight
from ml.ml40.features.properties.values.documents.jobs.generic_job import GenericJob
from ml.ml40.features.properties.values.documents.jobs.job import Job
from ml.ml40.features.properties.values.documents.jobs.job_list import JobList
from ml.ml40.features.properties.values.documents.jobs.job_status import JobStatus
from ml.ml40.features.properties.values.documents.reports.report import Report
Jiahang Chen's avatar
Jiahang Chen committed
80
from ml.ml40.features.properties.values.documents.reports.production_data import ProductionData
Jiahang Chen's avatar
Jiahang Chen committed
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

from ml.fml40.features.properties.values.abstract_inventory import AbstractInventory
from ml.fml40.features.properties.values.assortment import Assortment
from ml.fml40.features.properties.values.dbh import DBH
from ml.fml40.features.properties.values.harvesting_parameter import HarvestingParameters
from ml.fml40.features.properties.values.harvested_volume import HarvestedVolume
from ml.fml40.features.properties.values.inventory_data import InventoryData
from ml.fml40.features.properties.values.stem_segment_properties import StemSegmentProperties
from ml.fml40.features.properties.values.thickness_class import ThicknessClass
from ml.fml40.features.properties.values.tilt import Tilt
from ml.fml40.features.properties.values.tree_data import TreeData
from ml.fml40.features.properties.values.tree_type import TreeType
from ml.fml40.features.properties.values.wood_quality import WoodQuality

from ml.fml40.features.properties.values.documents.jobs.felling_job import FellingJob
from ml.fml40.features.properties.values.documents.jobs.fellung_support_job import FellingSupportJob
from ml.fml40.features.properties.values.documents.jobs.forwarding_job import ForwardingJob
from ml.fml40.features.properties.values.documents.jobs.log_transportation_job import LogTransportationJob
Jiahang Chen's avatar
Jiahang Chen committed
99

Jiahang Chen's avatar
Jiahang Chen committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

from ml.fml40.features.properties.values.documents.reports.afforestation_suggestion import AfforestationSuggestion
from ml.fml40.features.properties.values.documents.reports.felling_tool import FellingTool
from ml.fml40.features.properties.values.documents.reports.log_measurement import LogMeasurement
from ml.fml40.features.properties.values.documents.reports.log_transportation_report import LogTransportationReport
from ml.fml40.features.properties.values.documents.reports.map_data import MapData
from ml.fml40.features.properties.values.documents.reports.moisture_prediction_report import MoisturePredictionReport
from ml.fml40.features.properties.values.documents.reports.passability_report import PassabilityReport
from ml.fml40.features.properties.values.documents.reports.soil_moisture_measurement import SoilMoistureMeasurement

from ml.ml40.features.functionalities.accepts_jobs import AcceptsJobs
from ml.ml40.features.functionalities.accepts_reports import AcceptsReports
from ml.ml40.features.functionalities.clears_jobs import ClearsJobs
from ml.ml40.features.functionalities.functionality import Functionality
from ml.ml40.features.functionalities.manages_jobs import ManagesJobs
from ml.ml40.features.functionalities.plans_routes import PlansRoutes
from ml.ml40.features.functionalities.provides_map_data import ProvidesMapData
from ml.ml40.features.functionalities.provides_operational_data import ProvidesOperationalData
from ml.ml40.features.functionalities.renders import Renders

C. Albrecht's avatar
WIP  
C. Albrecht committed
120
from ml.fml40.features.functionalities.accepts_felling_jobs import AcceptsFellingJobs
Jiahang Chen's avatar
Jiahang Chen committed
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from ml.fml40.features.functionalities.accepts_felling_support_jobs import AcceptFellingSupportJobs
from ml.fml40.features.functionalities.accepts_log_measurements import AcceptsLogMeasurements
from ml.fml40.features.functionalities.accepts_log_transportaition_jobs import AcceptsLogTransportationJobs
from ml.fml40.features.functionalities.accepts_moisture_measurement import AcceptsMoistureMeasurement
from ml.fml40.features.functionalities.accepts_move_commands import AcceptsMoveCommands
from ml.fml40.features.functionalities.accepts_passability_report import AcceptsPassabilityReport
from ml.fml40.features.functionalities.accepts_proximity_alert import AcceptsProximityAlert
from ml.fml40.features.functionalities.accepts_single_tree_felling_jobs import AcceptsSingleTreeFellingJobs
from ml.fml40.features.functionalities.classifies_tree_species import ClassifiesTreeSpecies
from ml.fml40.features.functionalities.cuts import Cuts
from ml.fml40.features.functionalities.determines_passability import DeterminesPassability
from ml.fml40.features.functionalities.displays_health_alarms import DisplaysHealthAlarms
from ml.fml40.features.functionalities.evaluates_stand_attributes import EvaluatesStandAttributes
from ml.fml40.features.functionalities.fells import Fells
from ml.fml40.features.functionalities.forest_planning_evaluation import ForestPlanningEvaluation
C. Albrecht's avatar
WIP  
C. Albrecht committed
136
from ml.fml40.features.functionalities.forwards import Forwards
Jiahang Chen's avatar
Jiahang Chen committed
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from ml.fml40.features.functionalities.generates_afforestation_suggestions import GeneratesAfforestationSuggestions
from ml.fml40.features.functionalities.generates_felling_suggestions import GeneratesFellingSuggestions
from ml.fml40.features.functionalities.grabs import Grabs
from ml.fml40.features.functionalities.harvests import Harvests
from ml.fml40.features.functionalities.measure_wood import MeasuresWood
from ml.fml40.features.functionalities.monitor_health_status import MonitorsHealthStatus
from ml.fml40.features.functionalities.provides_moisture_prediction import ProvidesMoisturePrediction
from ml.fml40.features.functionalities.provides_passability_information import ProvidesPassabilityInformation
from ml.fml40.features.functionalities.provides_production_data import ProvidesProductionData
from ml.fml40.features.functionalities.provides_tree_data import ProvidesTreeData
from ml.fml40.features.functionalities.provides_weather_data import ProvidesWeatherData
from ml.fml40.features.functionalities.simulates_tree_growth import SimulatesTreeGrowth
from ml.fml40.features.functionalities.supports_felling import SupportsFelling
from ml.fml40.features.functionalities.transports_logs import TransportsLogs
C. Albrecht's avatar
WIP  
C. Albrecht committed
151
152

# TODO: Get rid of this global variable
Jiahang Chen's avatar
Jiahang Chen committed
153
# TODO: automatically get all classes in modul
C. Albrecht's avatar
WIP  
C. Albrecht committed
154
155
DT_FACTORY = {}

Jiahang Chen's avatar
Jiahang Chen committed
156
157
158
159
160
import sys, inspect

clsmembers = inspect.getmembers(sys.modules[__name__], inspect.isclass)
for member in clsmembers:
    DT_FACTORY[member[0]] = member[1]
C. Albrecht's avatar
WIP  
C. Albrecht committed
161

Jiahang Chen's avatar
Jiahang Chen committed
162

Jiahang Chen's avatar
Jiahang Chen committed
163
def build_sub_features(feature_ins, feature):
Jiahang Chen's avatar
Jiahang Chen committed
164
    """
Jiahang Chen's avatar
Jiahang Chen committed
165
    Instantiates and inserts ml40/fml40 sub features object into feature instance
C. Albrecht's avatar
WIP  
C. Albrecht committed
166

Jiahang Chen's avatar
Jiahang Chen committed
167
    :param feature_ins: ml40/fml40 feature instance, which has a sub feature to be built
Jiahang Chen's avatar
Jiahang Chen committed
168
    :type feature_ins: object
Jiahang Chen's avatar
Jiahang Chen committed
169
    :param feature: ml40/fml40 feature containing subFeatures
Jiahang Chen's avatar
Jiahang Chen committed
170
171
172
173
    :type feature: dict

    """
    sub_features = feature.get("subFeatures", [])
Jiahang Chen's avatar
Jiahang Chen committed
174
175
176
177
178
    for sub_f in sub_features:
        sub_f_name = sub_f.get("class", "")
        sub_f_obj = DT_FACTORY.get(remove_namespace(sub_f_name), None)
        if sub_f_obj is None:
            APP_LOGGER.critical("Subfeature: %s is missing" % sub_f_name)
C. Albrecht's avatar
WIP  
C. Albrecht committed
179
        else:
Jiahang Chen's avatar
Jiahang Chen committed
180
            APP_LOGGER.info("Adding subfeature: %s" % sub_f_name)
Jiahang Chen's avatar
Jiahang Chen committed
181
182
            sub_f_instance = sub_f_obj()
            for key in sub_f.keys():
Jiahang Chen's avatar
Jiahang Chen committed
183
                if key == "targets":
Jiahang Chen's avatar
Jiahang Chen committed
184
                    build_sub_thing(sub_f_instance, sub_f)
Jiahang Chen's avatar
Jiahang Chen committed
185
                elif key == "subFeatures":
Jiahang Chen's avatar
Jiahang Chen committed
186
                    build_sub_features(sub_f_instance, sub_f)
Jiahang Chen's avatar
Jiahang Chen committed
187
                else:
Jiahang Chen's avatar
Jiahang Chen committed
188
189
                    setattr(sub_f_instance, key, sub_f[key])
            feature_ins.subFeatures[sub_f_name] = sub_f_instance
Jiahang Chen's avatar
Jiahang Chen committed
190
191


Jiahang Chen's avatar
Jiahang Chen committed
192
193
def build_sub_thing(feature_ins, feature):
    """
Jiahang Chen's avatar
Jiahang Chen committed
194
    Instantiates and inserts sub thing object into a feature instance
Jiahang Chen's avatar
Jiahang Chen committed
195
196
197

    :param feature_ins: ml40/fml40 feature instance
    :type feature_ins: object
Jiahang Chen's avatar
Jiahang Chen committed
198
    :param feature: ml40/fml40 feature, which contains a sub thing
Jiahang Chen's avatar
Jiahang Chen committed
199
200
201
202
    :type feature: dict

    """
    json_sub_things = feature.get("targets", [])
Jiahang Chen's avatar
Jiahang Chen committed
203
    for json_sub_thing in json_sub_things:
Jiahang Chen's avatar
Jiahang Chen committed
204
        sub_thing_ref = create_thing(model={"attributes": json_sub_thing})
Jiahang Chen's avatar
Jiahang Chen committed
205
206
        sub_thing_name = json_sub_thing.get("name", None)
        feature_ins.targets[sub_thing_name] = sub_thing_ref
C. Albrecht's avatar
WIP  
C. Albrecht committed
207
208


Jiahang Chen's avatar
Jiahang Chen committed
209
def build(thing, attributes):
Jiahang Chen's avatar
Jiahang Chen committed
210
    """
Jiahang Chen's avatar
Jiahang Chen committed
211
    Builds a ml40 thing instance
Jiahang Chen's avatar
Jiahang Chen committed
212
213
214
215
216
217
218

    :param thing: ml40 thing instance
    :type thing: object
    :param attributes: attributes in ml40 thing json
    :type attributes: dict

    """
Jiahang Chen's avatar
Jiahang Chen committed
219
220
221
    if not isinstance(attributes, dict):
        APP_LOGGER.critical("Attributes is no valid json")
        return
Jiahang Chen's avatar
Jiahang Chen committed
222
    roles = attributes.get("roles", [])
Jiahang Chen's avatar
Jiahang Chen committed
223
    for role in roles:
Jiahang Chen's avatar
Jiahang Chen committed
224
225
        role_instance = build_role(role)
        thing.roles[role.get("class")] = role_instance
Jiahang Chen's avatar
Jiahang Chen committed
226

Jiahang Chen's avatar
Jiahang Chen committed
227
228
229
230
231
232
233
    json_features = attributes.get("features", [])
    for feature in json_features:
        feature_ins = build_feature(feature=feature)
        thing.features[feature.get("class")] = feature_ins


def build_role(role):
Jiahang Chen's avatar
Jiahang Chen committed
234
    """
Jiahang Chen's avatar
Jiahang Chen committed
235
     Instantiates and inserts a ml40/fml40 role object into a ml40 thing instance
Jiahang Chen's avatar
Jiahang Chen committed
236
237
238
239
240

    :param role: ml40/fml40 role
    :type role: dict

    """
Jiahang Chen's avatar
Jiahang Chen committed
241
242
243
244
    role_class_name = role.get("class", "")
    role_obj = DT_FACTORY.get(remove_namespace(role_class_name), None)
    if role_obj is None:
        APP_LOGGER.critical("Roles: %s is missing" % role_class_name)
Jiahang Chen's avatar
Jiahang Chen committed
245
        role_instance = None
Jiahang Chen's avatar
Jiahang Chen committed
246
    else:
Jiahang Chen's avatar
Jiahang Chen committed
247
        APP_LOGGER.info("Adding roles: %s" % role_class_name)
Jiahang Chen's avatar
Jiahang Chen committed
248
249
250
251
252
        role_instance = role_obj()
    return role_instance


def build_feature(feature):
Jiahang Chen's avatar
Jiahang Chen committed
253
    """
Jiahang Chen's avatar
Jiahang Chen committed
254
     Instantiates and inserts a ml40/fml40 feature object in a ml40 thing instance
Jiahang Chen's avatar
Jiahang Chen committed
255
256
257
258
259

    :param feature: ml40/fml40 feature
    :type feature: dict

    """
Jiahang Chen's avatar
Jiahang Chen committed
260
261
262
263
264
    feature_class_name = feature.get("class", "")
    feature_obj = DT_FACTORY.get(remove_namespace(feature_class_name), None)

    if feature_obj is None:
        APP_LOGGER.critical("Feature: %s is missing" % feature_class_name)
Jiahang Chen's avatar
Jiahang Chen committed
265
        feature_instance = None
Jiahang Chen's avatar
Jiahang Chen committed
266
    else:
Jiahang Chen's avatar
Jiahang Chen committed
267
        APP_LOGGER.info("Adding feature: %s" % feature_class_name)
Jiahang Chen's avatar
Jiahang Chen committed
268
269
        feature_instance = feature_obj()
        for key in feature.keys():
Jiahang Chen's avatar
Jiahang Chen committed
270
271
            if key == "class":
                continue
Jiahang Chen's avatar
Jiahang Chen committed
272
273
274
275
276
277
278
279
280
281
            if key == "targets":
                build_sub_thing(feature_instance, feature)
            elif key == "subFeatures":
                build_sub_features(feature_instance, feature)
            else:
                setattr(feature_instance, key, feature[key])
    return feature_instance


def add_function_impl_obj(thing, impl_obj, feature_name):
Jiahang Chen's avatar
Jiahang Chen committed
282
    """
Jiahang Chen's avatar
Jiahang Chen committed
283
    Adds user-specific implemented object to a thing instance
Jiahang Chen's avatar
Jiahang Chen committed
284
285
286
287
288
289
290
291
292
293

    :param thing: ml40 thing instance
    :type thing: object
    :param impl_obj: ml40/fml40 feature instance
    :type impl_obj: object
    :param feature_name: class name of a ml40/fml40 feature
    :type feature_name: str


    """
Jiahang Chen's avatar
Jiahang Chen committed
294
295
296
297
298
299
    feature = thing.features.get(feature_name, None)
    if feature is None:
        APP_LOGGER.critical(
            "Functionality %s is not one of the build-in functionalities" % feature_name
        )
    else:
Jiahang Chen's avatar
Jiahang Chen committed
300
        APP_LOGGER.info("Implementation object is added into the functionality %s" % feature_name)
Jiahang Chen's avatar
Jiahang Chen committed
301
302
303
        impl_ins = impl_obj()
        impl_ins.class_name = feature_name
        thing.features[feature_name] = impl_ins
Jiahang Chen's avatar
Jiahang Chen committed
304
305


Jiahang Chen's avatar
Jiahang Chen committed
306
307
308
def create_thing(model, grant_type="password",
                 secret="", username=None, password=None,
                 is_broker_rest=False, is_broker=False, is_repo=False):
Jiahang Chen's avatar
Jiahang Chen committed
309
    """
Jiahang Chen's avatar
Jiahang Chen committed
310
    Creates and launches a ml40 thing with the connection to S³I
Jiahang Chen's avatar
Jiahang Chen committed
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

    :param model: JSON of a ml40::thing model
    :type model: dict
    :param grant_type: grant type of OAuth2.0, which can be password or client_credentials
    :type grant_type: str
    :param secret: secret of a thing
    :type secret: str
    :param username: username, if the grant_type is set as password, the username is required
    :type username: str
    :param password: password, if the grant_type is set as password, the password is required
    :type password: str
    :param is_broker: whether broker interface is enabled in the ml40::thing instance
    :type is_broker: bool
    :param is_broker_rest: whether the broker interface uses the HTTP REST
    :type is_broker_rest: bool
    :param is_repo: whether the repository interface is enabled in the ml40::thing instance
    :type is_repo: bool

    :returns: ml40::thing instance
    :rtype: object

    """
C. Albrecht's avatar
WIP  
C. Albrecht committed
333
    attributes = model.get("attributes", None)
Jiahang Chen's avatar
Jiahang Chen committed
334

C. Albrecht's avatar
WIP  
C. Albrecht committed
335
    if attributes is None:
Jiahang Chen's avatar
Jiahang Chen committed
336
        sys.exit("Incomplete model: attributes missing!")
Jiahang Chen's avatar
Jiahang Chen committed
337
    thing_type = remove_namespace(attributes.get("class", ""))
C. Albrecht's avatar
WIP  
C. Albrecht committed
338

Jiahang Chen's avatar
Jiahang Chen committed
339
340
341
342
    roles = attributes.get("roles", None)
    if roles is None:
        sys.exit("Incomplete model: roles missing!")

C. Albrecht's avatar
WIP  
C. Albrecht committed
343
    thing_name = attributes.get("name", "")
Jiahang Chen's avatar
Jiahang Chen committed
344
    APP_LOGGER.info("Build digital twin {} with id {}".format(thing_name, model.get("thingId", "")))
Jiahang Chen's avatar
Jiahang Chen committed
345

Jiahang Chen's avatar
Jiahang Chen committed
346
    _thing = DT_FACTORY.get(thing_type)
Jiahang Chen's avatar
Jiahang Chen committed
347

Jiahang Chen's avatar
Jiahang Chen committed
348
    thing_ref = _thing(
C. Albrecht's avatar
WIP  
C. Albrecht committed
349
350
351
352
353
        model=model,
        grant_type=grant_type,
        client_secret=secret,
        username=username,
        password=password,
Jiahang Chen's avatar
Jiahang Chen committed
354
        is_broker_rest=is_broker_rest,
C. Albrecht's avatar
WIP  
C. Albrecht committed
355
        is_broker=is_broker,
Jiahang Chen's avatar
Jiahang Chen committed
356
        is_repo=is_repo
C. Albrecht's avatar
WIP  
C. Albrecht committed
357
358
359
    )
    build(thing_ref, attributes)
    return thing_ref