• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stlehmann / pyads / 20267830446

16 Dec 2025 12:25PM UTC coverage: 93.836% (+0.01%) from 93.826%
20267830446

push

github

web-flow
Add workaround for Beckhoff RT-Linux identification as FreeBSD (#479)

4 of 5 new or added lines in 1 file covered. (80.0%)

1781 of 1898 relevant lines covered (93.84%)

3.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.68
/src/pyads/utils.py
1
"""Utility functions.
2

3
:author: Stefan Lehmann <stlm@posteo.de>
4
:license: MIT, see license file or https://opensource.org/licenses/MIT
5
:created on: 2018-06-11 18:15:53
6

7
"""
8
import functools
4✔
9
import sys
4✔
10
import warnings
4✔
11
import os
4✔
12
from .structs import SAdsSymbolEntry
4✔
13
from .constants import PLC_STRING_TYPE_NAME, PLC_DEFAULT_STRING_SIZE
4✔
14

15
from typing import Callable, Any, Optional
4✔
16

17

18
def platform_is_beckhoff_rt_linux() -> bool:
4✔
19
    """Workaround to identify Beckhoff RT-Linux as a FreeBSD-based solution instead of Linux."""
20
    return os.path.exists("/usr/bin/TcSystemServiceUm")
4✔
21

22

23
def platform_is_linux() -> bool:
4✔
24
    """Return True if current platform is Linux or Mac OS."""
25
    return (sys.platform.startswith("linux") or sys.platform.startswith("darwin")) and not platform_is_beckhoff_rt_linux()
4✔
26

27

28
def platform_is_windows() -> bool:
4✔
29
    """Return True if current platform is Windows."""
30
    # cli being .NET (IronPython)
31
    return sys.platform == "win32" or sys.platform == "cli"
4✔
32

33

34
def platform_is_freebsd() -> bool:
4✔
35
    """Return True if current platform is FreeBSD."""
NEW
36
    return sys.platform.startswith("freebsd") or platform_is_beckhoff_rt_linux()
×
37

38

39
def deprecated(message: Optional[str] = None) -> Callable:
4✔
40
    """Decorator for deprecated functions.
41

42
    Shows a deprecation warning with the given message if the
43
    decorated function is called.
44

45
    """
46
    if message is None:
4✔
47
        message = "Deprecated. This function will not be available in future versions."
4✔
48

49
    def decorator(func: Callable) -> Callable:
4✔
50
        @functools.wraps(func)
4✔
51
        def wrapper(*args: Any, **kwargs: Any) -> Callable:
4✔
52
            warnings.warn(message, DeprecationWarning)  # type: ignore
4✔
53
            return func(*args, **kwargs)
4✔
54

55
        return wrapper
4✔
56

57
    return decorator
4✔
58

59

60
def decode_ads(message: bytes) -> str:
4✔
61
    """
62
    Decode a string that in encoded in the format used by ADS.
63

64
    From Beckhoff documentation: 'A STRING constant is a string enclosed by
65
    single quotation marks. The characters are encoded according to the Windows
66
    1252 character set. As a subset of Windows-1252, the character set of
67
    ISO/IEC 8859-1 is supported.'
68
    """
69
    return message.decode("windows-1252").strip(" \t\n\r\0")
4✔
70

71

72
def find_wstring_null_terminator(data: bytearray) -> Optional[int]:
4✔
73
    """Find null-terminator in WSTRING (UTF-16) data.
74

75
    :return: None if no null-terminator was found, else the index of the null-terminator
76

77
    """
78
    for ix in range(1, len(data), 2):
4✔
79
        if (data[ix - 1], data[ix]) == (0, 0):
4✔
80
            return ix - 1
4✔
81
    else:
82
        return None
4✔
83

84
def get_num_of_chars(symbol_type_str: SAdsSymbolEntry.symbol_type) -> int:
4✔
85
    """Gets the number of characters in a Beckhoff string using the symbol type str.
86

87
    TODO: Find this information some other way without string manipulation of the symbol type?
88

89
    Args:
90
        symbol_type_str (SAdsSymbolEntry.symbol_type): Symbol type of a string, wstring, or a string array
91

92
    Returns:
93
        num_characters (int): The number of characters im the string
94
    """
95

96
    # Find "STRING" in type 
97
    pos_of_string_in_name = symbol_type_str.upper().find(PLC_STRING_TYPE_NAME)
4✔
98

99
    # If not string return -1
100
    if pos_of_string_in_name == -1:
4✔
101
        return(-1)
4✔
102

103
    # Generate start index based on the position of PLC_STRING_TYPE_NAME in the array
104
    start_index = pos_of_string_in_name + len(PLC_STRING_TYPE_NAME) + 1
4✔
105

106
    # Return the value cast as int
107
    try:
4✔
108
        return(int(symbol_type_str[start_index:-1]))
4✔
109
    except ValueError:
×
110
        return(PLC_DEFAULT_STRING_SIZE)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc