def wait_for_test_session_start( mgr: sl_tag.TagManager, after_time: datetime.datetime = None ) -> None: """Wait for a recently-started FlexLogger test session to actually start.""" if after_time is None: after_time = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta( seconds=-2 ) # Wait for LastTestSessionStart to be updated by FlexLogger tag_path = get_tag_prefix() + ".Export.System.LastTestSessionStart" tag_reader = mgr.get_tag_reader(tag_path, sl_tag.DataType.DATE_TIME) while True: read_result = tag_reader.read(include_timestamp=True) if read_result is not None and read_result.value > after_time: assert read_result.timestamp is not None last_start = read_result.timestamp break time.sleep(0.5) # In FlexLogger 2020 R3+, the timestamps of the two values will be the same. In # older versions, IsTestSessionRunning is updated second. tag_path = get_tag_prefix() + ".Export.System.IsTestSessionRunning" tag_reader2 = mgr.get_tag_reader(tag_path, sl_tag.DataType.BOOLEAN) while True: read_result2 = tag_reader2.read(include_timestamp=True) if read_result2 is not None and read_result2.value is True: assert read_result2.timestamp is not None if read_result2.timestamp >= last_start: return time.sleep(0.5)
def start_test_session(mgr: sl_tag.TagManager) -> None: """Start a FlexLogger test session.""" writer = mgr.create_writer(buffer_size=1) path = get_tag_prefix() + ".Export.System.IsTestSessionRunning" already_running = mgr.read(path) if already_running is not None and already_running.value is True: raise RuntimeError("There is already a test session running") writer.write(path, sl_tag.DataType.BOOLEAN, True)
def create_output_channel(mgr: sl_tag.TagManager, group: str, name: str, data_type: sl_tag.DataType) -> sl_tag.TagData: """Create a FlexLogger output channel.""" # "Import" the channel into FlexLogger. full_name = get_tag_prefix() + ".Import.Setpoint.{}.{}".format(group, name) mgr.open(full_name, data_type, create=True) # Once FlexLogger creates the channel, we'll interact with it as an "export" channel # (for both reads and writes). full_name = get_tag_prefix() + ".Export.Setpoint.{}".format(name) # Go ahead and pre-create the output channel, for ease-of-use. Otherwise, when # trying to read its value, we'd have to be prepared for an ApiException complaining # that the tag doesn't exist. mgr.open(full_name, data_type, create=True) return sl_tag.TagData(full_name, data_type)
def print_tags(mgr: sl_tag.TagManager, prefix="") -> None: """Print FlexLogger tags that have the given prefix. By default, no prefix is included, so all FlexLogger tags are printed. To restrict it to just tags *exported* by FlexLogger, use prefix="Export.". """ tag_prefix = get_tag_prefix() + "." + prefix with mgr.open_selection([tag_prefix + "*"]) as channels: # # Uncomment to hide system tags, such as IsTestSessionRunning # chan_names = [name for name in chan_names if ".Export.System." not in name] # Hide the tag prefix chan_names = [name.replace(tag_prefix, "") for name in channels.values] if len(chan_names) == 0: print( "No channels reported to NI SystemLink; check your project settings" ) return max_name_len = max(len(name) for name in chan_names) def format_row( name: str, typ: Union[str, None], value: Any, timestamp: Union[datetime.datetime, str, None], ) -> str: columns = ( name.ljust(max_name_len), str(timestamp).ljust(32), str(typ).ljust(9), str(value).ljust(9), ) return " ".join(columns) print(format_row("Name", "Type", "Value", "Timestamp")) for name in sorted(chan_names, key=str.casefold): reader = channels.values[tag_prefix + name] read_result = reader.read(include_timestamp=True) value = read_result.value if read_result is not None else None data_type = read_result.data_type.name if read_result is not None else None timestamp = read_result.timestamp if read_result is not None else None print(format_row(name, data_type, value, timestamp))
def wait_forever_for_tag_changes(mgr: sl_tag.TagManager, tags: List[sl_tag.TagData]) -> None: """Watch for tag changes, and print any updates to the console. This method will never return. """ def on_tag_changed(tag: sl_tag.TagData, reader: Optional[sl_tag.TagValueReader]) -> None: if reader is not None: read_result = reader.read() value = read_result.value if read_result is not None else None print("Value changed to {}".format(value)) with mgr.create_selection(tags) as selection: with selection.create_subscription() as subscription: subscription.tag_changed += on_tag_changed try: while True: time.sleep(100) except KeyboardInterrupt: return
def _simulate_temp_chamber(mgr: sl_tag.TagManager) -> None: # noqa: D103 # fl = flexlogger.Client() # .buffered_channel_writer is a thin wrapper around systemlink.tag.BufferedTagWriter # - this wrapper takes TagData objects instead of `path` and `datatype` parameters # - maybe I'll instead modify systemlink.tag.BufferedTagWriter to accept a TagData writer = mgr.create_writer(buffer_size=10) chans = [ create_channel(mgr, "Temperature Chamber", "Ceiling", sl_tag.DataType.DOUBLE), create_channel(mgr, "Temperature Chamber", "Door", sl_tag.DataType.DOUBLE), create_channel(mgr, "Temperature Chamber", "Floor", sl_tag.DataType.DOUBLE), ] vals = [78.0, 76.0, 74.0] try: while True: now = datetime.datetime.now() for chan, val in zip(chans, vals): writer.write(chan.path, chan.data_type, val, timestamp=now) writer.send_buffered_writes() time.sleep(0.1) vals = [v + random.normalvariate(0, 0.2) for v in vals] except KeyboardInterrupt: pass
def create_channel( mgr: sl_tag.TagManager, group: str, name: str, data_type: sl_tag.DataType ) -> sl_tag.TagData: """Create a FlexLogger channel.""" tag_path = get_tag_prefix() + ".Import.{}.{}".format(group, name) return mgr.open(tag_path, data_type, create=True)
from datetime import timedelta from systemlink.clients.tag import DataType, TagManager mgr = TagManager() tag = mgr.open("MyTags.Example Tag", DataType.DOUBLE, create=True) with mgr.create_writer(buffer_size=10, max_buffer_time=timedelta(seconds=3)) as writer: writer.write(tag.path, tag.data_type, 3.5) # Note: Exiting the "with" block automatically calls writer.send_buffered_writes() read_result = mgr.read(tag.path) assert read_result is not None assert read_result.value == 3.5
path = tag.path data_type = tag.data_type.name if reader is not None: read_result = reader.read() # A read_result of None means that the tag has no value, but it *must* # have a value, because we got a tag_changed event! assert read_result is not None value = read_result.value else: value = "???" # tag has unknown data type print(f'Tag changed: "{path}" = {value} ({data_type})') mgr = TagManager() if SIMULATE_EXTERNAL_TAG_CHANGES: mgr.open("MyTags.Example Tag", DataType.DOUBLE, create=True) writer = mgr.create_writer(buffer_size=1) with ExitStack() as stack: # Notes: # 1. The tags are assumed to already exist before this example is run, but # setting SIMULATE_EXTERNAL_TAG_CHANGES to True will ensure there is one. # 2. Any tags that get added later will NOT automatically appear in the # selection just because the path matches the wildcard used below; you # must call one of the selection's refresh methods to update the tag list # from the server. But even if you do that: # 3. The subscription will only contain the tags that were in the selection # when the subscription was created. If you want the subscription to add # new tags that were added to the selection, you must recreate it.
def stop_test_session(mgr: sl_tag.TagManager) -> None: """Stop the current FlexLogger test session.""" writer = mgr.create_writer(buffer_size=1) path = get_tag_prefix() + ".Export.System.IsTestSessionRunning" writer.write(path, sl_tag.DataType.BOOLEAN, False)