Skip to content

Commit

Permalink
Merge pull request #50 from arilieb/feat-windows
Browse files Browse the repository at this point in the history
Windows Compatibility
  • Loading branch information
SmithSamuelM authored Feb 28, 2025
2 parents 17301d4 + 9bfc5ef commit 9ef12b6
Show file tree
Hide file tree
Showing 15 changed files with 534 additions and 236 deletions.
12 changes: 7 additions & 5 deletions src/hio/base/filing.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,15 @@ class Filer(hioing.Mixin):
stat.S_IWUSR Owner has write permission.
stat.S_IXUSR Owner has execute permission.
"""
HeadDirPath = "/usr/local/var" # default in /usr/local/var
HeadDirPath = os.path.join(os.path.sep, 'usr', 'local', 'var') # default in /usr/local/var
TailDirPath = "hio"
CleanTailDirPath = "hio/clean"
AltHeadDirPath = "~" # put in ~ as fallback when desired not permitted
CleanTailDirPath = os.path.join("hio", "clean")


AltHeadDirPath = os.path.expanduser("~") # put in ~ as fallback when desired not permitted
AltTailDirPath = ".hio"
AltCleanTailDirPath = ".hio/clean"
TempHeadDir = "/tmp"
AltCleanTailDirPath = os.path.join(".hio", "clean")
TempHeadDir = os.path.join(os.path.sep, "tmp")
TempPrefix = "hio_"
TempSuffix = "_test"
Perm = stat.S_ISVTX | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR # 0o1700==960
Expand Down
5 changes: 4 additions & 1 deletion src/hio/core/coring.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import subprocess
import socket
import platform

#import netifaces # netifaces2

Expand All @@ -20,6 +21,8 @@ def normalizeHost(host):
"""
if host == "":
host = "0.0.0.0"
if platform.system() == "Windows":
host = "127.0.0.1"

try: # try ipv4
info = socket.getaddrinfo(host,
Expand All @@ -28,7 +31,7 @@ def normalizeHost(host):
socket.SOCK_DGRAM,
socket.IPPROTO_IP, 0)
except socket.gaierror as ex: # try ipv6
if host in ("", "0.0.0.0"):
if host in ("", "0.0.0.0", "127.0.0.1"):
host = "::"

info = socket.getaddrinfo(host,
Expand Down
161 changes: 131 additions & 30 deletions src/hio/core/serial/serialing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import os
import errno
import platform
from collections import deque


Expand Down Expand Up @@ -87,12 +88,30 @@ def open(self, port=''):
To debug use os.isatty(fd) which returns True if the file descriptor
fd is open and connected to a tty-like device, else False.
On UNIX/macOS systems uses os.ctermid() to get console
On Windows systems uses console directly via msvcrt
"""
if not port:
port = os.ctermid() # default to console
system = platform.system()
if system == 'Windows':
# Windows doesn't need a specific port for console access via msvcrt
port = None
else:
# Unix/macOS use ctermid
port = os.ctermid() # default to console

try:
self.fd = os.open(port, os.O_NONBLOCK | os.O_RDWR | os.O_NOCTTY)
if platform.system() == 'Windows':
# Windows handling through msvcrt
import msvcrt
# For Windows, we'll use stdin/stdout file descriptors
# and rely on msvcrt for non-blocking operations
self.fd = 0 # Use 0 as a placeholder value for Windows console
# No specific open operation needed for Windows console via msvcrt
else:
# Unix/macOS handling
self.fd = os.open(port, os.O_NONBLOCK | os.O_RDWR | os.O_NOCTTY)
except OSError as ex:
logger.error("Error opening console serial port, %s\n", ex)
return False
Expand All @@ -115,7 +134,7 @@ def close(self):
"""
if self.fd:
os.close(self.fd)
self.fd = None
self.fd = None
del self.rxbs[:]
self.opened = False

Expand All @@ -124,7 +143,16 @@ def put(self, data = b'\n'):
"""
Writes data bytes to console and return number of bytes from data written.
"""
return (os.write(self.fd, data)) # returns number of bytes written
if platform.system() == 'Windows':
# Windows-specific console output
import msvcrt
# Write bytes to stdout
for b in data:
msvcrt.putch(bytes([b]))
return len(data)
else:
# Unix/macOS
return os.write(self.fd, data) # returns number of bytes written


def get(self, bs=None):
Expand All @@ -142,8 +170,19 @@ def get(self, bs=None):
"""
bs = bs if bs is not None else self.bs
line = bytearray()

try:
self.rxbs.extend(os.read(self.fd, bs))
if platform.system() == 'Windows':
# Windows-specific non-blocking read using msvcrt
import msvcrt
# Check if any keys are available
while msvcrt.kbhit():
# Read a character and add it to our buffer
char = msvcrt.getch()
self.rxbs.extend(char)
else:
# Unix/macOS non-blocking read
self.rxbs.extend(os.read(self.fd, bs))
except OSError as ex1: # if no chars available generates exception
# ex.args[0] == ex.errno for better os compatibility
# the value of a given errno.XXXXX may be different on each os
Expand All @@ -156,11 +195,14 @@ def get(self, bs=None):
" '%s'\n", self.fd, ex1)
raise # re-raise exception ex1


else:
if (idx := self.rxbs.find(ord(b'\n'))) != -1:
line.extend(self.rxbs[:idx]) # copy all but newline
del self.rxbs[:idx+1] # delete including newline
# Process any complete lines in the buffer
if (idx := self.rxbs.find(ord(b'\n'))) != -1:
line.extend(self.rxbs[:idx]) # copy all but newline
del self.rxbs[:idx+1] # delete including newline
elif platform.system() == 'Windows' and (idx := self.rxbs.find(ord(b'\r'))) != -1:
# On Windows, handle CR as line terminator too
line.extend(self.rxbs[:idx]) # copy all but CR
del self.rxbs[:idx+1] # delete including CR

return line

Expand Down Expand Up @@ -296,7 +338,6 @@ class Device():
Use instance methods get & put to read & write to serial device
Needs os module
"""

def __init__(self, port=None, speed=9600, bs=1024):
"""
Initialization method for instance.
Expand All @@ -306,7 +347,15 @@ def __init__(self, port=None, speed=9600, bs=1024):
bs = buffer size for reads
"""
self.fd = None # serial device port file descriptor, must be opened first
self.port = port or os.ctermid() #default to console
self.port = port

if not self.port:
system = platform.system()
if system == 'Windows':
self.port = 'COM1' # Default Windows serial port
else:
self.port = os.ctermid() # Default to console on Unix/macOS

self.speed = speed or 9600
self.bs = bs or 1024
self.opened = False
Expand Down Expand Up @@ -372,9 +421,27 @@ def setraw(fd, when=TCSAFLUSH):
if bs is not None:
self.bs = bs

self.fd = os.open(self.port, os.O_NONBLOCK | os.O_RDWR | os.O_NOCTTY)

system = platform.system()

if system == 'Windows':
try:
# Try to use pyserial for COM ports on Windows for better handling
import serial
self._serial = serial.Serial(port=self.port,
baudrate=self.speed,
timeout=0)
# Placeholder for fd
self.fd = 0
except ImportError:
# Fall back to basic file operations if pyserial is not available
self.fd = os.open(self.port, os.O_RDWR | os.O_BINARY)
import msvcrt
msvcrt.setmode(self.fd, os.O_BINARY)
self._serial = None
else:
# Unix/macOS handling
self.fd = os.open(self.port, os.O_NONBLOCK | os.O_RDWR | os.O_NOCTTY)
self._serial = None

if (system == 'Darwin') or (system == 'Linux'): # use termios to set values
import termios
Expand Down Expand Up @@ -419,12 +486,17 @@ def setraw(fd, when=TCSAFLUSH):
def close(self):
"""
Closes fd.
"""
if self.fd:
if self._serial:
self._serial.close()
self._serial = None

if self.fd and platform.system() != 'Windows' or not self._serial:
# Close fd if not Windows or if we didn't use pyserial
os.close(self.fd)
self.fd = None
self.opened = False

self.fd = None
self.opened = False


def receive(self):
Expand All @@ -435,13 +507,28 @@ def receive(self):
"""
data = b''
try:
data = os.read(self.fd, self.bs) #if no chars available generates exception
if platform.system() == 'Windows':
if self._serial:
# Use pyserial if available
data = self._serial.read(self.bs)
else:
# Fall back to msvcrt for console
import msvcrt
if msvcrt.kbhit():
char = msvcrt.getch()
data = char
else:
data = os.read(self.fd, self.bs)
else:
# Unix/macOS read
data = os.read(self.fd, self.bs) #if no chars available generates exception
except OSError as ex1: # ex1 is the target instance of the exception
if ex1.errno == errno.EAGAIN: #BSD 35, Linux 11
# BSD 35, Linux 11
if ex1.errno == errno.EAGAIN or (platform.system() == 'Windows' and ex1.errno == errno.EWOULDBLOCK):
pass #No characters available
else:
logger.error("Error: Receive on Device '%s'."
" '%s'\n", self.port, ex)
" '%s'\n", self.port, ex1)
raise #re raise exception ex1

return data
Expand All @@ -452,13 +539,18 @@ def send(self, data=b'\n'):
Returns number of bytes sent
"""
try:
count = os.write(self.fd, data)
if platform.system() == 'Windows' and self._serial:
# Use pyserial if available
count = self._serial.write(data)
else:
count = os.write(self.fd, data)
except OSError as ex1: # ex1 is the target instance of the exception
if ex1.errno == errno.EAGAIN: # BSD 35, Linux 11
# BSD 35, Linux 11
if ex1.errno == errno.EAGAIN or (platform.system() == 'Windows' and ex1.errno == errno.EWOULDBLOCK):
count = 0 # buffer full can't write
else:
logger.error("Error: Send on Device '%s'."
" '%s'\n", self.port, ex)
" '%s'\n", self.port, ex1)
raise #re raise exception ex1

return count
Expand All @@ -473,7 +565,6 @@ class Serial():
Use instance methods get & put to read & write to serial device
Needs os module
"""

def __init__(self, port=None, speed=9600, bs=1024):
"""
Initialization method for instance.
Expand All @@ -485,7 +576,15 @@ def __init__(self, port=None, speed=9600, bs=1024):
"""
self.serial = None # Serial instance
self.port = port or os.ctermid() #default to console
self.port = port

if not self.port:
system = platform.system()
if system == 'Windows':
self.port = 'COM1' # Default Windows serial port
else:
self.port = os.ctermid() # Default to console on Unix/macOS

self.speed = speed or 9600
self.bs = bs or 1024
self.opened = False
Expand Down Expand Up @@ -540,11 +639,12 @@ def receive(self):
try:
data = self.serial.read(self.bs) #if no chars available generates exception
except OSError as ex1: # ex1 is the target instance of the exception
if ex1.errno == errno.EAGAIN: #BSD 35, Linux 11
# BSD 35, Linux 11
if ex1.errno == errno.EAGAIN or (platform.system() == 'Windows' and ex1.errno == errno.EWOULDBLOCK):
pass #No characters available
else:
logger.error("Error: Receive on Serial '%s'."
" '%s'\n", self.port, ex)
" '%s'\n", self.port, ex1)
raise #re raise exception ex1

return data
Expand All @@ -557,11 +657,12 @@ def send(self, data=b'\n'):
try:
count = self.serial.write(data)
except OSError as ex1: # ex1 is the target instance of the exception
if ex1.errno == errno.EAGAIN: #BSD 35, Linux 11
# BSD 35, Linux 11
if ex1.errno == errno.EAGAIN or (platform.system() == 'Windows' and ex1.errno == errno.EWOULDBLOCK):
count = 0 # buffer full can't write
else:
logger.error("Error: Send on Serial '%s'."
" '%s'\n", self.port, ex)
" '%s'\n", self.port, ex1)
raise #re raise exception ex1

return count
Expand Down
6 changes: 4 additions & 2 deletions src/hio/core/wiring.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ class WireLog():
"""
Prefix = "hio"
HeadDirPath = "/usr/local/var" # default in /usr/local/var

HeadDirPath = os.path.join(os.path.sep, 'usr', 'local', 'var') # default in /usr/local/var

TailDirPath = "wirelogs"
AltHeadDirPath = "~" # put in ~ as fallback when desired dir not permitted
TempHeadDir = "/tmp"
TempHeadDir = os.path.join(os.path.sep, "tmp")
TempPrefix = "test_"
TempSuffix = "_temp"
Format = b'\n%(dx)b %(who)b:\n%(data)b\n' # default format string as bytes
Expand Down
Loading

0 comments on commit 9ef12b6

Please sign in to comment.