def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    try:
        executor = None
        parser = build_args()
        args = parser.parse_args(args=argv)
        model = DeviceModel()
        parser = SensorGraphFileParser()
        parser.parse_file(args.sensor_graph)
        parser.compile(model)
        if not args.disable_optimizer:
            opt = SensorGraphOptimizer()
            opt.optimize(parser.sensor_graph, model=model)
        graph = parser.sensor_graph
        sim = SensorGraphSimulator(graph)
        for stop in args.stop:
            sim.stop_condition(stop)
        for watch in args.watch:
            watch_sel = DataStreamSelector.FromString(watch)
            graph.sensor_log.watch(watch_sel, watch_printer)
        if args.semihost_device is not None:
            executor = SemihostedRPCExecutor(args.port, args.semihost_device)
            sim.rpc_executor = executor
        for mock in args.mock_rpc:
            slot, rpc_id, value = process_mock_rpc(mock)
            sim.rpc_executor.mock(slot, rpc_id, value)
        for stim in args.stimulus:
            sim.stimulus(stim)
        graph.load_constants()
        if args.trace is not None:
            sim.record_trace()
        try:
            if args.connected:
                sim.step(user_connected, 8)
            sim.run(accelerated=not args.realtime)
        except KeyboardInterrupt:
            pass
        if args.trace is not None:
            sim.trace.save(args.trace)
    finally:
        if executor is not None:
            executor.hw.close()
    return 0