Ejemplo n.º 1
0
    def test_date_time_tag(self):
        if _debug: TestNameValue._debug("test_date_time_tag")

        # BACnet Birthday (close)
        date_time = DateTime(date=(95, 1, 25, 3), time=(9, 0, 0, 0))
        if _debug: TestNameValue._debug("    - date_time: %r", date_time)

        # try the primitive types
        name_value_endec("start", date_time)
Ejemplo n.º 2
0
def create_DateTimeValue(oid=1,
                         date=None,
                         time=None,
                         name="DateTime",
                         pv_writable=False):
    datetime = DateTimeValueObject(objectIdentifier=("datetimeValue", oid),
                                   objectName=name)
    datetime = _make_mutable(datetime, mutable=pv_writable)
    datetime.presentValue = DateTime(date=Date(date), time=Time(time))
    return datetime
Ejemplo n.º 3
0
    def build_rrange_request(
        self, args, range_params=None, arr_index=None, vendor_id=0, bacoid=None
    ):
        addr, obj_type, obj_inst, prop_id = args[:4]

        vendor_id = vendor_id
        bacoid = bacoid

        if obj_type.isdigit():
            obj_type = int(obj_type)
        elif not get_object_class(obj_type, vendor_id=vendor_id):
            raise ValueError("Unknown object type {}".format(obj_type))

        obj_inst = int(obj_inst)

        if prop_id.isdigit():
            prop_id = int(prop_id)
        datatype = get_datatype(obj_type, prop_id, vendor_id=vendor_id)
        if not datatype:
            raise ValueError("invalid property for object type")

        # build a request
        request = ReadRangeRequest(
            objectIdentifier=(obj_type, obj_inst), propertyIdentifier=prop_id
        )
        request.pduDestination = Address(addr)
        if range_params is not None:
            range_type, first, date, time, count = range_params
            if range_type == "p":
                rbp = RangeByPosition(referenceIndex=int(first), count=int(count))
                request.range = Range(byPosition=rbp)
            elif range_type == "s":
                rbs = RangeBySequenceNumber(
                    referenceSequenceNumber=int(first), count=int(count)
                )
                request.range = Range(bySequenceNumber=rbs)
            elif range_type == "t":
                rbt = RangeByTime(
                    referenceTime=DateTime(
                        date=Date(date).value, time=Time(time).value
                    ),
                    count=int(count),
                )
                request.range = Range(byTime=rbt)
            elif range_type == "x":
                # should be missing required parameter
                request.range = Range()
            else:
                raise ValueError("unknown range type: %r" % (range_type,))

        if len(args) == 5:
            request.propertyArrayIndex = int(args[4])
        self._log.debug("{:<20} {!r}".format("REQUEST", request))
        return request
Ejemplo n.º 4
0
def datetime_value(**kwargs):
    definition = {
        "name": "DATETIME_VALUE",
        "objectType": DateTimeValueObject,
        "instance": 0,
        "description": "No description",
        "presentValue": DateTime(),
        "properties": {},
        "is_commandable": False,
        "relinquish_default": "inactive",
    }
    return _create(definition, **kwargs)
Ejemplo n.º 5
0
    def test_8_10_1(self):
        """Confirmed Notifications Subscription"""
        if _debug: TestPulseConverter._debug("test_8_10_1")

        # create a network
        anet = ApplicationNetwork("test_8_10_1")

        # add the ability to accept COV notifications to the TD
        anet.td.add_capability(COVTestClientServices)

        # tell the TD how to respond to confirmed notifications
        anet.td.test_ack = True
        anet.td.test_reject = None
        anet.td.test_abort = None

        # add the service capability to the IUT
        anet.iut.add_capability(ChangeOfValueServices)

        # make a pulse converter object
        test_pc = PulseConverterObject(
            objectIdentifier=('pulseConverter', 1),
            objectName='pc',
            presentValue=0.0,
            statusFlags=[0, 0, 0, 0],
            updateTime=DateTime(date=Date().now().value, time=Time().now().value),
            covIncrement=10.0,
            covPeriod=10,
            )

        # add it to the implementation
        anet.iut.add_object(test_pc)

        # wait for the subscription
        anet.iut.start_state.doc("8.10.1-1-0") \
            .receive(SubscribeCOVRequest).doc("8.10.1-1-1") \
            .success()

        # send the subscription, wait for the ack
        anet.td.start_state.doc("8.10.1-2-0") \
            .send(SubscribeCOVRequest(
                destination=anet.iut.address,
                subscriberProcessIdentifier=1,
                monitoredObjectIdentifier=('pulseConverter', 1),
                issueConfirmedNotifications=True,
                lifetime=30,
                )).doc("8.10.1-2-1") \
            .receive(SimpleAckPDU).doc("8.10.1-2-2") \
            .success()

        # run the group
        anet.run()
Ejemplo n.º 6
0
    def process_task(self):
        if _debug: PulseTask._debug("process_task")

        # increment the present value
        self.accumulator.presentValue += self.increment

        # update the value change time
        current_date = Date().now().value
        current_time = Time().now().value

        value_change_time = DateTime(date=current_date, time=current_time)
        if _debug: PulseTask._debug("    - value_change_time: %r", value_change_time)

        self.accumulator.valueChangeTime = value_change_time
Ejemplo n.º 7
0
    def ReadProperty(self, obj, arrayIndex=None):
        if _debug: CurrentDateTimeProperty._debug("ReadProperty %r arrayIndex=%r", obj, arrayIndex)

        # access an array
        if arrayIndex is not None:
            raise ExecutionError(errorClass='property', errorCode='propertyIsNotAnArray')

        # get the value
        current_date = Date().now().value
        current_time = Time().now().value

        value = DateTime(date=current_date, time=current_time)
        if _debug: CurrentDateTimeProperty._debug("    - value: %r", value)

        return value
Ejemplo n.º 8
0
    def _set_value(self, utc_time):
        # convert to a time tuple based on timezone offset
        time_tuple = time.gmtime(utc_time + timezone_offset)

        # extra the pieces
        date_quad = (
            time_tuple[0] - 1900,
            time_tuple[1],
            time_tuple[2],
            time_tuple[6] + 1,
        )
        time_quad = (time_tuple[3], time_tuple[4], time_tuple[5], 0)

        date_time = DateTime(date=date_quad, time=time_quad)

        self.presentValue = date_time
Ejemplo n.º 9
0
def _build_datetime(UTC=False):
    if UTC:
        _d = dt.datetime.utcnow().date()
        _t = dt.datetime.utcnow().time()
        _date = Date(year=_d.year - 1900,
                     month=_d.month,
                     day=_d.day,
                     day_of_week=_d.isoweekday()).value
        _time = Time(
            hour=_t.hour,
            minute=_t.minute,
            second=_t.second,
            hundredth=int(_t.microsecond / 10000),
        ).value
    else:
        _date = Date().now().value
        _time = Time().now().value
    return DateTime(date=_date, time=_time)
Ejemplo n.º 10
0
    def test_no_traffic(self):
        """Test basic configuration of a network."""
        if _debug: TestPulseConverter._debug("test_no_traffic")

        # create a network
        anet = ApplicationNetwork("test_no_traffic")

        # add the service capability to the IUT
        anet.iut.add_capability(ChangeOfValueServices)

        # make a pulse converter object
        test_pc = PulseConverterObject(
            objectIdentifier=('pulseConverter', 1),
            objectName='pc',
            presentValue=0.0,
            statusFlags=[0, 0, 0, 0],
            updateTime=DateTime(date=Date().now().value, time=Time().now().value),
            covIncrement=10.0,
            covPeriod=10,
            )

        # an easy way to change the present value
        write_test_pc = lambda v: setattr(test_pc, 'presentValue', v)

        # add it to the implementation
        anet.iut.add_object(test_pc)

        # make some transitions
        anet.iut.start_state.doc("1-1-0") \
            .call(write_test_pc, 100.0).doc("1-1-1") \
            .timeout(1).doc("1-1-2") \
            .call(write_test_pc, 0.0).doc("1-1-3") \
            .timeout(1).doc("1-1-4") \
            .success()

        # test device is quiet
        anet.td.start_state.timeout(5).success()

        # run the group
        anet.run()
Ejemplo n.º 11
0
def main():
    global this_application

    # parse the command line arguments
    args = ConfigArgumentParser(description=__doc__).parse_args()

    if _debug:
        _log.debug("initialization")
    if _debug:
        _log.debug("    - args: %r", args)

    # make a device object
    this_device = LocalDeviceObject(ini=args.ini)
    if _debug:
        _log.debug("    - this_device: %r", this_device)

    # make a simple application
    this_application = ReadRangeApplication(this_device, args.ini.address)

    timestamp = DateTime(date=Date().now().value, time=Time().now().value)
    # log_status = LogStatus([0,0,0])
    log_record_datum = LogRecordLogDatum(booleanValue=False)
    status_flags = StatusFlags([0, 0, 0, 0])
    log_record = LogRecord(timestamp=timestamp,
                           logDatum=log_record_datum,
                           statusFlags=status_flags)

    trend_log_object = TrendLogObject(
        objectIdentifier=("trendLog", 1),
        objectName="Trend-Log-1",
        logBuffer=[log_record],
    )
    _log.debug("    - trend_log_object: %r", trend_log_object)
    this_application.add_object(trend_log_object)

    _log.debug("running")

    run()

    _log.debug("fini")
Ejemplo n.º 12
0
    def sometime(self, klass, now, args):
        if _debug: TestConsoleCmd._debug("sometime %r %r %r", klass, now, args)

        try:
            addr = args[0]

            # look for a time to send
            if (len(args) > 1):
                when = time.strptime(' '.join(args[1:]), TIME_FORMAT)
            else:
                when = now
            if _debug: TestConsoleCmd._debug("    - when: %r", when)

            # build the date and time primitives
            when_date = Date(
                year=when.tm_year - 1900,
                month=when.tm_mon,
                day=when.tm_mday,
                dayOfWeek=when.tm_wday + 1,
            )
            if _debug: TestConsoleCmd._debug("    - when_date: %s", when_date)
            when_time = Time(hour=when.tm_hour,
                             minute=when.tm_min,
                             second=when.tm_sec,
                             hundredth=0)
            if _debug: TestConsoleCmd._debug("    - when_time: %s", when_time)

            # build a request
            request = klass()
            request.pduDestination = Address(addr)

            # only one simple parameter, happens to be the same for both types
            request.time = DateTime(date=when_date, time=when_time)

            # give it to the application
            this_application.request(request)

        except Exception, e:
            TestConsoleCmd._exception("exception: %r", e)
Ejemplo n.º 13
0
    def _set_value(self, utc_time):
        if _debug:
            LocalDateTimeValueObject._debug("_set_value %r", utc_time)

        # convert to a time tuple based on timezone offset
        time_tuple = time.gmtime(utc_time + timezone_offset)
        if _debug:
            LocalDateTimeValueObject._debug("    - time_tuple: %r", time_tuple)

        # extra the pieces
        date_quad = (
            time_tuple[0] - 1900,
            time_tuple[1],
            time_tuple[2],
            time_tuple[6] + 1,
        )
        time_quad = (time_tuple[3], time_tuple[4], time_tuple[5], 0)

        date_time = DateTime(date=date_quad, time=time_quad)
        if _debug:
            LocalDateTimeValueObject._debug("    - date_time: %r", date_time)

        self.presentValue = date_time
Ejemplo n.º 14
0
    def test_8_2_1(self):
        """To verify that the IUT can initiate ConfirmedCOVNotification service
        requests conveying a change of the Present_Value property of Analog
        Input, Analog Output, and Analog Value objects."""
        if _debug: TestPulseConverter._debug("test_8_2_1")

        # create a network
        anet = ApplicationNetwork("test_8_2_1")

        # add the ability to accept COV notifications to the TD
        anet.td.add_capability(COVTestClientServices)

        # tell the TD how to respond to confirmed notifications
        anet.td.test_ack = True
        anet.td.test_reject = None
        anet.td.test_abort = None

        # add the service capability to the IUT
        anet.iut.add_capability(ChangeOfValueServices)

        # make a pulse converter object
        test_pc = PulseConverterObject(
            objectIdentifier=('pulseConverter', 1),
            objectName='pc',
            presentValue=0.0,
            statusFlags=[0, 0, 0, 0],
            updateTime=DateTime(date=Date().now().value, time=Time().now().value),
            covIncrement=10.0,
            covPeriod=10,
            )

        # an easy way to change the present value
        def write_test_pc(v):
            if _debug: TestPulseConverter._debug("=== marco %r", v)
            setattr(test_pc, 'presentValue', v)
            if _debug: TestPulseConverter._debug("=== polo")

        # add it to the implementation
        anet.iut.add_object(test_pc)

        # receive the subscription request, wait until the client has
        # received the ack and the 'instant' notification.  Then change the
        # value a little bit and nothing should be sent.  Change it some more
        # and wait for the notification ack.
        anet.iut.start_state.doc("2-1-0") \
            .receive(SubscribeCOVRequest).doc("2-1-1") \
            .receive(SimpleAckPDU).doc("2-1-2") \
            .wait_event("e1").doc("2-1-3") \
            .call(write_test_pc, 5.0).doc("2-1-4") \
            .timeout(5).doc("2-1-5") \
            .call(write_test_pc, 10.0).doc("2-1-6") \
            .receive(SimpleAckPDU).doc("2-1-7") \
            .receive(SimpleAckPDU).doc("2-1-8") \
            .timeout(10).doc("2-1-9") \
            .success()

        # send the subscription request, wait for the ack and the 'instant'
        # notification, set the event so the IUT can continue, then wait
        # for the next notification
        anet.td.start_state.doc("2-2-0") \
            .send(SubscribeCOVRequest(
                destination=anet.iut.address,
                subscriberProcessIdentifier=1,
                monitoredObjectIdentifier=('pulseConverter', 1),
                issueConfirmedNotifications=True,
                lifetime=30,
                )).doc("2-2-1") \
            .receive(SimpleAckPDU).doc("2-2-2") \
            .receive(ConfirmedCOVNotificationRequest).doc("2-2-3") \
            .set_event("e1").doc("2-2-4") \
            .receive(ConfirmedCOVNotificationRequest).doc("2-2-5") \
            .receive(ConfirmedCOVNotificationRequest).doc("2-2-6") \
            .timeout(10).doc("2-2-7") \
            .success()

        # run the group
        anet.run()
Ejemplo n.º 15
0
    def do_readrange(self, args):
        """readrange <addr> <objid> <prop> [ <indx> ]
            [ p <indx> <count> ]
            [ s <seq> <count> ]
            [ t <date> <time> <count> ]
        """
        args = args.split()
        if _debug:
            ReadRangeConsoleCmd._debug("do_readrange %r", args)

        try:
            addr = Address(args.pop(0))
            obj_id = ObjectIdentifier(args.pop(0)).value
            prop_id = args.pop(0)

            datatype = get_datatype(obj_id[0], prop_id)
            if not datatype:
                raise ValueError("invalid property for object type")

            # build a request
            request = ReadRangeRequest(destination=addr,
                                       objectIdentifier=obj_id,
                                       propertyIdentifier=prop_id)

            # index is optional
            if args:
                if args[0].isdigit():
                    if not issubclass(datatype, Array):
                        raise ValueError("property is not an array")
                    request.propertyArrayIndex = int(args.pop(0))
                    datatype = datatype.subtype
            if not issubclass(datatype, List):
                raise ValueError("property is not a list")

            # range is optional
            if args:
                range_type = args.pop(0)
                if range_type == "p":
                    rbp = RangeByPosition(referenceIndex=int(args[0]),
                                          count=int(args[1]))
                    request.range = Range(byPosition=rbp)
                elif range_type == "s":
                    rbs = RangeBySequenceNumber(referenceSequenceNumber=int(
                        args[0]),
                                                count=int(args[1]))
                    request.range = Range(bySequenceNumber=rbs)
                elif range_type == "t":
                    rbt = RangeByTime(
                        referenceTime=DateTime(date=Date(args[0]),
                                               time=Time(args[1])),
                        count=int(args[2]),
                    )
                    request.range = Range(byTime=rbt)
                else:
                    raise ValueError("unknown range type: %r" % (range_type, ))

            if _debug:
                ReadRangeConsoleCmd._debug("    - request: %r", request)

            # make an IOCB
            iocb = IOCB(request)
            if _debug:
                ReadRangeConsoleCmd._debug("    - iocb: %r", iocb)

            # give it to the application
            deferred(this_application.request_io, iocb)

            # wait for it to complete
            iocb.wait()

            # do something for success
            if iocb.ioResponse:
                apdu = iocb.ioResponse
                if _debug:
                    ReadRangeConsoleCmd._debug("    - apdu: %r", apdu)

                # should be an ack
                if not isinstance(apdu, ReadRangeACK):
                    if _debug:
                        ReadRangeConsoleCmd._debug("    - not an ack")
                    return

                # find the datatype
                datatype = get_datatype(apdu.objectIdentifier[0],
                                        apdu.propertyIdentifier)
                if _debug:
                    ReadRangeConsoleCmd._debug("    - datatype: %r", datatype)
                if not datatype:
                    raise TypeError("unknown datatype")

                sys.stdout.write("firstSequenceNumber: %s\n" %
                                 (apdu.firstSequenceNumber, ))
                sys.stdout.write("resultFlags: %s\n" % (apdu.resultFlags, ))

                # cast out the data into a list
                value = apdu.itemData.cast_out(datatype)

                # dump it out
                for i, item in enumerate(value):
                    sys.stdout.write("[%d]\n" % (i, ))
                    item.debug_contents(file=sys.stdout, indent=2)
                sys.stdout.flush()

            # do something for error/reject/abort
            if iocb.ioError:
                sys.stdout.write(str(iocb.ioError) + "\n")

        except Exception as error:
            ReadRangeConsoleCmd._exception("exception: %r", error)
Ejemplo n.º 16
0
    def time_sync(self, *args, datetime=None):
        """
        Take local time and send it to devices. User can also provide
        a datetime value (constructed following bacpypes.basetypes.Datetime
        format).

        To create a DateTime ::

            from bacpypes.basetypes import DateTime
            from bacpypes.primitivedata import Date, Time

            # Create date and time
            _date = Date('2019-08-05')
            _time = Time('16:45')

            # Create Datetime
            _datetime = DateTime(date=_date.value, time=_time.value)

            # Pass this to the function
            bacnet.time_sync(datetime=_datetime)


        """
        if not self._started:
            raise ApplicationNotStarted(
                "BACnet stack not running - use startApp()")

        if args:
            args = args[0].split()
        msg = args if args else "everyone"

        self._log.debug("time sync {!r}".format(msg))

        if not datetime:
            _date = Date().now().value
            _time = Time().now().value
            _datetime = DateTime(date=_date, time=_time)
        elif isinstance(datetime, DateTime):
            _datetime = datetime
        else:
            raise ValueError(
                "Please provide valid DateTime in bacpypes.basetypes.DateTime format"
            )

        # build a request
        request = TimeSynchronizationRequest(time=_datetime)
        if len(args) == 1:
            request.pduDestination = Address(args[0])
            del args[0]
        else:
            request.pduDestination = GlobalBroadcast()

        self._log.debug("{:>12} {}".format("- request:", request))

        iocb = IOCB(request)  # make an IOCB

        # pass to the BACnet stack
        deferred(self.this_application.request_io, iocb)

        # Unconfirmed request...so wait until complete
        iocb.wait()  # Wait for BACnet response
        year, month, day, dow = _datetime.date
        year = year + 1900
        hour, minutes, sec, msec = _datetime.time
        d = dt.datetime(year, month, day, hour, minutes, sec, msec)
        self._log.info("Time Sync Request sent to network : {}".format(
            d.isoformat()))
Ejemplo n.º 17
0
    def test_changing_properties(self):
        """This test changes the value of multiple properties to verify that
        only one COV notification is sent."""
        if _debug: TestPulseConverter._debug("test_changing_properties")

        # create a network
        anet = ApplicationNetwork("test_changing_properties")

        # add the ability to accept COV notifications to the TD
        anet.td.add_capability(COVTestClientServices)

        # tell the TD how to respond to confirmed notifications
        anet.td.test_ack = True
        anet.td.test_reject = None
        anet.td.test_abort = None

        # add the service capability to the IUT
        anet.iut.add_capability(ChangeOfValueServices)

        # make a pulse converter object
        test_pc = PulseConverterObject(
            objectIdentifier=('pulseConverter', 1),
            objectName='pc',
            presentValue=0.0,
            statusFlags=[0, 0, 0, 0],
            updateTime=DateTime(date=Date().now().value, time=Time().now().value),
            covIncrement=10.0,
            covPeriod=10,
            )

        # an easy way to change the present value
        def test_pc_fault():
            if _debug: TestPulseConverter._debug("test_pc_fault")
            test_pc.presentValue = 100.0
            test_pc.statusFlags = [0, 0, 1, 0]

        # add it to the implementation
        anet.iut.add_object(test_pc)

        # receive the subscription request, wait until the client has
        # received the ack and the 'instant' notification.  Then change the
        # value, no ack coming back
        anet.iut.start_state.doc("5-1-0") \
            .receive(SubscribeCOVRequest).doc("5-1-1") \
            .wait_event("e1").doc("5-1-2") \
            .call(test_pc_fault).doc("5-1-3") \
            .timeout(10).doc("5-2-4") \
            .success()

        # test device is quiet
        anet.td.start_state.doc("5-2-0") \
            .send(SubscribeCOVRequest(
                destination=anet.iut.address,
                subscriberProcessIdentifier=1,
                monitoredObjectIdentifier=('pulseConverter', 1),
                issueConfirmedNotifications=False,
                lifetime=30,
                )).doc("5-2-1") \
            .receive(SimpleAckPDU).doc("5-2-2") \
            .receive(UnconfirmedCOVNotificationRequest).doc("5-2-3") \
            .set_event("e1").doc("5-2-4") \
            .receive(UnconfirmedCOVNotificationRequest).doc("5-2-5") \
            .timeout(10).doc("5-2-6") \
            .success()

        # run the group
        anet.run()
Ejemplo n.º 18
0
    def test_8_10_2(self):
        """Unconfirmed Notifications Subscription"""
        if _debug: TestPulseConverter._debug("test_8_10_2")

        # create a network
        anet = ApplicationNetwork("test_8_10_2")

        # add the ability to accept COV notifications to the TD
        anet.td.add_capability(COVTestClientServices)

        # tell the TD how to respond to confirmed notifications
        anet.td.test_ack = True
        anet.td.test_reject = None
        anet.td.test_abort = None

        # add the service capability to the IUT
        anet.iut.add_capability(ChangeOfValueServices)

        # make a pulse converter object
        test_pc = PulseConverterObject(
            objectIdentifier=('pulseConverter', 1),
            objectName='pc',
            presentValue=0.0,
            statusFlags=[0, 0, 0, 0],
            updateTime=DateTime(date=Date().now().value, time=Time().now().value),
            covIncrement=10.0,
            covPeriod=10,
            )

        # add it to the implementation
        anet.iut.add_object(test_pc)

        # wait for the subscription
        anet.iut.start_state.doc("8.10.2-1-0") \
            .receive(SubscribeCOVRequest).doc("8.10.2-1-1") \
            .success()

        # send the subscription, wait for the ack
        anet.td.start_state.doc("8.10.2-2-0") \
            .send(SubscribeCOVRequest(
                destination=anet.iut.address,
                subscriberProcessIdentifier=1,
                monitoredObjectIdentifier=('pulseConverter', 1),
                issueConfirmedNotifications=False,
                lifetime=30,
                )).doc("8.10.2-2-1") \
            .receive(SimpleAckPDU).doc("8.10.2-2-2") \
            .success()

        # run the group, cut the time limit short
        anet.run(time_limit=5.0)

        # check that the IUT still has the detection
        if _debug: TestPulseConverter._debug("    - detections: %r", anet.iut.cov_detections)
        assert len(anet.iut.cov_detections) == 1

        # pop out the subscription list and criteria
        obj_ref, criteria = anet.iut.cov_detections.popitem()
        if _debug: TestPulseConverter._debug("    - criteria: %r", criteria)

        # get the list of subscriptions from the criteria
        subscriptions = criteria.cov_subscriptions.cov_subscriptions
        if _debug: TestPulseConverter._debug("    - subscriptions: %r", subscriptions)
        assert len(subscriptions) == 1
Ejemplo n.º 19
0
    def test_multiple_subscribers(self):
        """This has more than one subscriber for the object."""
        if _debug: TestPulseConverter._debug("test_multiple_subscribers")

        # create a network
        anet = ApplicationNetwork("test_multiple_subscribers")

        # add the ability to accept COV notifications to the TD
        anet.td.add_capability(COVTestClientServices)

        # tell the TD how to respond to confirmed notifications
        anet.td.test_ack = True
        anet.td.test_reject = None
        anet.td.test_abort = None

        # add the service capability to the IUT
        anet.iut.add_capability(ChangeOfValueServices)

        # make a pulse converter object
        test_pc = PulseConverterObject(
            objectIdentifier=('pulseConverter', 1),
            objectName='pc',
            presentValue=0.0,
            statusFlags=[0, 0, 0, 0],
            updateTime=DateTime(date=Date().now().value, time=Time().now().value),
            covIncrement=10.0,
            covPeriod=10,
            )

        # an easy way to change both the present value and status flags
        # which should trigger only one notification
        def test_pc_fault():
            if _debug: TestPulseConverter._debug("test_pc_fault")
            test_pc.presentValue = 100.0
            test_pc.statusFlags = [0, 0, 1, 0]

        # add it to the implementation
        anet.iut.add_object(test_pc)

        # add another test device object
        anet.td2_device_object = LocalDeviceObject(
            objectName="td2",
            objectIdentifier=('device', 30),
            maxApduLengthAccepted=1024,
            segmentationSupported='noSegmentation',
            vendorIdentifier=999,
            )

        # another test device
        anet.td2 = ApplicationStateMachine(anet.td2_device_object, anet.vlan)
        anet.td2.add_capability(COVTestClientServices)
        anet.append(anet.td2)

        # receive the subscription requests, wait until both clients have
        # received the ack and the 'instant' notification.  Then change the
        # value, no ack coming back
        anet.iut.start_state.doc("6-1-0") \
            .receive(SubscribeCOVRequest, pduSource=anet.td.address).doc("6-1-1") \
            .receive(SubscribeCOVRequest, pduSource=anet.td2.address).doc("6-1-2") \
            .wait_event("e2").doc("6-1-3") \
            .call(test_pc_fault).doc("6-1-4") \
            .timeout(10).doc("6-2-5") \
            .success()

        # first test device; send the subscription request, get an ack
        # followed by the 'instant' notification
        anet.td.start_state.doc("6-2-0") \
            .send(SubscribeCOVRequest(
                destination=anet.iut.address,
                subscriberProcessIdentifier=1,
                monitoredObjectIdentifier=('pulseConverter', 1),
                issueConfirmedNotifications=False,
                lifetime=30,
                )).doc("6-2-1") \
            .receive(SimpleAckPDU).doc("6-2-2") \
            .receive(UnconfirmedCOVNotificationRequest).doc("6-2-3") \
            .set_event("e1").doc("6-2-4") \
            .receive(UnconfirmedCOVNotificationRequest).doc("6-2-5") \
            .timeout(10).doc("6-2-6") \
            .success()

        # same pattern for the other test device
        anet.td2.start_state.doc("6-3-0") \
            .wait_event("e1").doc("6-3-1") \
            .send(SubscribeCOVRequest(
                destination=anet.iut.address,
                subscriberProcessIdentifier=1,
                monitoredObjectIdentifier=('pulseConverter', 1),
                issueConfirmedNotifications=False,
                lifetime=30,
                )).doc("6-3-2") \
            .receive(SimpleAckPDU).doc("6-3-3") \
            .receive(UnconfirmedCOVNotificationRequest).doc("6-3-4") \
            .set_event("e2").doc("6-3-5") \
            .receive(UnconfirmedCOVNotificationRequest).doc("6-3-6") \
            .timeout(10).doc("6-3-7") \
            .success()

        # run the group
        anet.run()