Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ad40a3b
chore: update pyright config
Hugovdberg Mar 29, 2025
1dc7c67
chore: add .gitkeep to docs folders
Hugovdberg Mar 31, 2025
079d5cf
chore: add jupyter checkpoints and notebooks in the root
Hugovdberg Mar 31, 2025
664e2c5
chore: add debug env to pixi config with jupyter lab
Hugovdberg Mar 31, 2025
a19147e
fix: import fake SDK directly during type checking
Hugovdberg Mar 31, 2025
31c0dfe
feat: expand type hints for AF SDK
Hugovdberg Mar 31, 2025
31201ad
feat!: rename _time to Time and import AFSDK as SDK
Hugovdberg Mar 31, 2025
bc83e71
feat!: move PIAFElement and PIAFTable to Asset, add Search
Hugovdberg Mar 31, 2025
5c395ac
chore: simplify Search.__call__
Hugovdberg Apr 2, 2025
2f42e5e
feat: add AFAttribute.stepped_data and AFBaseElement.path properties
Hugovdberg Apr 2, 2025
29c1ae0
feat: complete DataContainerCollection data queries
Hugovdberg Apr 2, 2025
3101b19
feat: migrate collections to new module, rename PIAF to AF
Hugovdberg Apr 4, 2025
39e3851
feat: add type hints for item getters
Hugovdberg Apr 4, 2025
448799b
feat: complete migration AF to _collections
Hugovdberg Apr 4, 2025
7361129
feat: add PIPoint.Step
Hugovdberg Apr 4, 2025
c943797
chore: AF to SDK.AF
Hugovdberg Apr 4, 2025
8a323fa
chore: merge/rename modules
Hugovdberg Apr 4, 2025
31f8d9d
feat: implement explicit loader for SDK
Hugovdberg Jun 25, 2024
f7f3cf9
chore: update SDK documentation root to Aveva site
Hugovdberg Jun 25, 2024
7629f00
feat: added PIconnector to __init__, added protocols for module spec
Hugovdberg Jun 25, 2024
8e205e4
test: move skip on CI marker to common.py
Hugovdberg Jun 25, 2024
f10c925
chore: replace deprecated type hints
Hugovdberg Mar 29, 2025
352fd22
chore: cleanup dotnet mocks
Hugovdberg Apr 8, 2025
a8c56f1
chore: cleanup _utils.py
Hugovdberg Apr 8, 2025
d0cc595
feat!: move AFEventFrame to separate module
Hugovdberg Apr 8, 2025
2b1b286
feat: Move AuthenticationMode to PI.py
Hugovdberg Apr 14, 2025
2366d70
fix: create empty valuelist for empty collection
Hugovdberg Apr 14, 2025
a0167da
feat!: separate dotnet type hints and actual libraries
Hugovdberg Apr 14, 2025
0358bef
Merge pull request #818 from Hugovdberg/add_search
Hugovdberg Apr 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Jupyter notebooks
*.ipynb_checkpoints
/*.ipynb

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
164 changes: 164 additions & 0 deletions PIconnect/AF.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
"""AF - Core containers for connections to the PI Asset Framework."""

import logging
import warnings
from typing import Any, Self

from PIconnect import Asset, EventFrame, Search, Time, dotnet

_logger = logging.getLogger(__name__)
_DEFAULT_EVENTFRAME_SEARCH_MODE = EventFrame.EventFrameSearchMode.STARTING_AFTER


class AFDatabase:
"""Context manager for connections to the PI Asset Framework database."""

version = "0.3.0"

@classmethod
def servers(cls) -> dict[str, dotnet.AF.PISystem]:
"""Return a dictionary of the known servers."""
return {server.Name: server for server in dotnet.lib.AF.PISystems()}

@classmethod
def default_server(cls) -> dotnet.AF.PISystem | None:
"""Return the default server."""
if dotnet.lib.AF.PISystems().DefaultPISystem:
return dotnet.lib.AF.PISystems().DefaultPISystem
servers = dotnet.lib.AF.PISystems()
if servers.Count > 0:
return next(iter(servers))
else:
return None

def __init__(self, server: str | None = None, database: str | None = None) -> None:
self.server = self._initialise_server(server)
self.database = self._initialise_database(database)
self.search = Search.Search(self.database)

def _initialise_server(self, server: str | None) -> dotnet.AF.PISystem:
"""Initialise the server connection."""
_logger.debug(f"Initialising server connection from {server!r}")
default_server = self.default_server()
if server is None:
if default_server is None:
raise ValueError("No server specified and no default server found.")
_logger.debug(f"Using default server: {default_server.Name}")
return default_server

if (_server := dotnet.lib.AF.PISystems()[server]) is not None:
_logger.debug(_server)
return _server
else:
if default_server is None:
raise ValueError(
f'Server "{server}" not found and no default server found.'
) from None
message = f'Server "{server}" not found, using the default server.'
_logger.debug(message)
warnings.warn(message=message, category=UserWarning, stacklevel=2)
return default_server

def _initialise_database(self, database: str | None) -> dotnet.AF.AFDatabase:
def default_db():
default = self.server.Databases.DefaultDatabase
if default is None:
raise ValueError("No database specified and no default database found.")
return default

if database is None:
return default_db()

if (_db := self.server.Databases[database]) is not None:
_logger.debug(_db)
return _db
else:
message = f'Database "{database}" not found, using the default database.'
warnings.warn(message=message, category=UserWarning, stacklevel=2)
return default_db()

def __enter__(self) -> Self:
"""Open the PI AF server connection context."""
self.server.Connect()
return self

def __exit__(
self,
*args: Any, # type: ignore
) -> bool:
"""Close the PI AF server connection context."""
_logger.log(0, f"Closing connection to {self} ({args=})")
return False
# Disabled disconnecting because garbage collection sometimes impedes
# connecting to another server later
# self.server.Disconnect()

def __repr__(self) -> str:
"""Return a representation of the PI AF database connection."""
return f"{self.__class__.__qualname__}(\\\\{self.server_name}\\{self.database_name})"

@property
def server_name(self) -> str:
"""Return the name of the connected PI AF server."""
return self.server.Name

@property
def database_name(self) -> str:
"""Return the name of the connected PI AF database."""
return self.database.Name

@property
def children(self) -> dict[str, Asset.AFElement]:
"""Return a dictionary of the direct child elements of the database."""
return {c.Name: Asset.AFElement(c) for c in self.database.Elements}

@property
def tables(self) -> dict[str, Asset.AFTable]:
"""Return a dictionary of the tables in the database."""
return {t.Name: Asset.AFTable(t) for t in self.database.Tables}

def descendant(self, path: str) -> Asset.AFElement:
"""Return a descendant of the database from an exact path."""
return Asset.AFElement(self.database.Elements.get_Item(path))

def event_frames(
self,
start_time: Time.TimeLike = "",
start_index: int = 0,
max_count: int = 1000,
search_mode: EventFrame.EventFrameSearchMode = _DEFAULT_EVENTFRAME_SEARCH_MODE,
search_full_hierarchy: bool = False,
) -> dict[str, EventFrame.AFEventFrame]:
"""Search for event frames in the database."""
_start_time = Time.to_af_time(start_time)
_search_mode = dotnet.lib.AF.EventFrame.AFEventFrameSearchMode(int(search_mode))
return {
frame.Name: EventFrame.AFEventFrame(frame)
for frame in dotnet.lib.AF.EventFrame.AFEventFrame.FindEventFrames(
self.database,
None,
_start_time,
start_index,
max_count,
_search_mode,
None,
None,
None,
None,
search_full_hierarchy,
)
}


class PIAFDatabase(AFDatabase):
"""Context manager for connections to the PI Asset Framework database."""

version = "0.3.0"

def __init__(self, server: str | None = None, database: str | None = None) -> None:
warnings.warn(
"PIAFDatabase is deprecated, use AFDatabase instead.",
DeprecationWarning,
stacklevel=2,
)
super().__init__(server=server, database=database)
77 changes: 0 additions & 77 deletions PIconnect/AFSDK.py

This file was deleted.

Loading
Loading