import logging
from ctypes import byref, c_char_p, c_int32, c_uint8, CFUNCTYPE, sizeof
from .core import PSP, get_psp_exc_msg
from .exc import (
PSPError,
PSPNotExist,
PSPInvalid,
PSPNotOpened,
PSPNotSupport,
)
from .lmbinc import (
ERR_Invalid,
ERR_NotExist,
ERR_NotOpened,
ERR_NotSupport,
ERR_Success,
LCMInfo,
LCMKeyMsg,
)
from .sdk_dll import DLL
logger = logging.getLogger(__name__)
SUPPORTED_PLATFORMS = ("NCA-2510",)
UNSUPPORTED_PLATFORMS = ("LEB-2680", "LEB-7242", "LEC-2290", "LEC-7230", "V3S", "V6S",)
DEFAULT_LCM_PORT = "/dev/ttyS1"
DEFAULT_BAUD_RATE = 19200
LCM_UART_TYPE = 1
LCM_LPT_TYPE = 2
[docs]
class LCM:
"""
LCD Module.
:param bool check_platform:
Set to :data:`True` to check if the platform supports this feature.
Defaults to :data:`False` for better compatibility.
:raises PSPNotSupport: This function is not supported
(when ``check_platform`` is set to :data:`True`).
:raises NotImplementedError: It has not been verified to run on this platform
(when ``check_platform`` is set to :data:`True`).
"""
def __init__(self, check_platform: bool = False) -> None:
self._str_lcm_port = c_char_p(DEFAULT_LCM_PORT.encode())
self._dw_speed = DEFAULT_BAUD_RATE
self._version = DLL().get_version()
if not check_platform:
return
if self._version.platform_id in SUPPORTED_PLATFORMS:
pass
elif self._version.platform_id in UNSUPPORTED_PLATFORMS:
raise PSPNotSupport("Not supported on this platform")
else:
raise NotImplementedError
[docs]
def search_port(self) -> str:
"""
Get the LCM current connected port and speed.
:return: the port and speed the LCM is currently connected to
:rtype: str
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPNotExist: This function is not enabled or does not exist.
:raises PSPError: General PSP functional error.
"""
# TODO: Example
dw_speed = c_int32(self._dw_speed)
with PSP() as psp:
if self._version.dll_major == 2 and self._version.dll_minor in (1, 2, 3):
i_ret = psp.lib.LMB_LCM_SearchPort(self._str_lcm_port, byref(dw_speed))
elif self._version.dll_major == 3 and self._version.dll_minor in (0,):
i_ret = psp.lib.LMB_LCM_SearchPort(self._str_lcm_port, byref(dw_speed), sizeof(self._str_lcm_port))
else:
raise NotImplementedError
msg = get_psp_exc_msg("LMB_LCM_SearchPort", i_ret)
if i_ret == ERR_Success:
return f"port={self._str_lcm_port}, speed={dw_speed.value:d}"
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
elif i_ret == ERR_NotExist:
raise PSPNotExist(msg)
else:
raise PSPError(msg)
def _open_port(self, psp: PSP) -> None:
"""
Open the LCM device with path and assigned speed.
.. Note::
* Support only **UART** type.
* The string maximum length depends on LCD module, normally 40 characters.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPNotExist: This function is not enabled or does not exist.
:raises PSPError: General PSP functional error.
"""
i_ret = psp.lib.LMB_LCM_OpenPort(self._str_lcm_port, self._dw_speed)
msg = get_psp_exc_msg("LMB_LCM_OpenPort", i_ret)
if i_ret == ERR_Success:
pass
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
elif i_ret == ERR_NotExist:
raise PSPNotExist(msg)
else:
raise PSPError(msg)
def _open_device(self, psp: PSP) -> None:
"""
Open and connect LCD module.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPNotExist: This function is not enabled or does not exist.
:raises PSPError: General PSP functional error.
"""
i_ret = psp.lib.LMB_LCM_DeviceOpen()
msg = get_psp_exc_msg("LMB_LCM_DeviceOpen", i_ret)
if i_ret == ERR_Success:
pass
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
elif i_ret == ERR_NotExist:
raise PSPNotExist(msg)
else:
raise PSPError(msg)
def _close_device(self, psp: PSP) -> None:
"""
Close and disconnect LCD module.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: General PSP functional error.
"""
i_ret = psp.lib.LMB_LCM_DeviceClose()
msg = get_psp_exc_msg("LMB_LCM_DeviceClose", i_ret)
if i_ret == ERR_Success:
pass
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
def _get_device_info(self, psp: PSP) -> int:
"""
Get platform LCM device information.
:return: LCM module type (UART = 1, LPT = 2)
:rtype: int
"""
stu_lcm_info = LCMInfo()
i_ret = psp.lib.LMB_LCM_DeviceInfo(byref(stu_lcm_info))
msg = get_psp_exc_msg("LMB_LCM_DeviceInfo", i_ret)
if i_ret == ERR_Success:
logger.debug(f"LCM Mode No. is {stu_lcm_info.uw_mode_no:04X}\n"
f"LCM Firmware Ver. is {stu_lcm_info.uw_version:04X}\n"
f"LCM Speed ={stu_lcm_info.udw_baud_rate:d}")
return LCM_UART_TYPE
else:
# ERR_NotOpened or ERR_NotSupport.
logger.debug(msg)
return LCM_LPT_TYPE
[docs]
def reset(self) -> None:
"""
Reset the LCD module.
.. Note::
Support only **UART** type.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: General PSP functional error.
"""
# TODO: Example
with PSP() as psp:
self._open_port(psp)
if self._get_device_info(psp) == LCM_LPT_TYPE:
raise PSPNotSupport("LPT type not support reset")
i_ret = psp.lib.LMB_LCM_Reset()
msg = get_psp_exc_msg("LMB_LCM_Reset", i_ret)
if i_ret == ERR_Success:
pass
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
self._close_device(psp)
[docs]
def set_backlight(self, enable: bool) -> None:
"""
Set LCD module light on/off status.
Example:
.. code-block:: pycon
>>> lcm = LCM()
>>> lcm.set_backlight(True)
:param bool enable: set :data:`True` to enable backlight, otherwise :data:`False`
:raises TypeError: The input parameters type error.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: General PSP functional error.
"""
# Check type.
if not isinstance(enable, bool):
raise TypeError("'enable' type must be bool")
# Run.
with PSP() as psp:
self._open_device(psp)
i_ret = psp.lib.LMB_LCM_LightCtrl(c_uint8(enable & 0xFF).value)
msg = get_psp_exc_msg("LMB_LCM_LightCtrl", i_ret)
if i_ret == ERR_Success:
logger.debug(f"set LCM backlight to {enable}")
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
self._close_device(psp)
[docs]
def set_cursor(self, row: int, column: int = 1) -> None:
"""
Set LCM cursor.
Example:
.. code-block:: pycon
>>> lcm = LCM()
>>> lcm.set_cursor(2, 6)
:param int row: row value of LCM display cursor
:param int column: column value of LCM display cursor
:raises TypeError: The input parameters type error.
:raises PSPInvalid: The input parameter is out of range.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: General PSP functional error.
"""
# Check type.
if not isinstance(row, int):
raise TypeError("'row' type must be int")
if not isinstance(column, int):
raise TypeError("'column' type must be int")
# Check value has been done by the PSP.
# Run.
with PSP() as psp:
self._open_device(psp)
i_ret = psp.lib.LMB_LCM_SetCursor(column, row)
msg = get_psp_exc_msg("LMB_LCM_SetCursor", i_ret)
if i_ret == ERR_Success:
logger.debug(f"set LCM cursor to row {row} column {column}")
elif i_ret == ERR_Invalid:
raise PSPInvalid(msg)
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
self._close_device(psp)
[docs]
def write(self, msg: str) -> None:
"""
Write string to LCD module.
.. Note::
The maximum string length depends on the LCD module, normally 40 characters.
Example:
.. code-block:: pycon
>>> lcm = LCM()
>>> lcm.write("Hello World")
:param str msg: message string to LCM display
:raises TypeError: The input parameters type error.
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: General PSP functional error.
"""
# Check type.
if not isinstance(msg, str):
raise TypeError("'msg' type must be str")
# Run.
with PSP() as psp:
self._open_device(psp)
i_ret = psp.lib.LMB_LCM_WriteString(c_char_p(msg.encode()))
msg = get_psp_exc_msg("LMB_LCM_WriteString", i_ret)
if i_ret == ERR_Success:
logger.debug(f"write '{msg}' on LCM")
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
self._close_device(psp)
[docs]
def clear(self) -> None:
"""
Clear LCM display.
Example:
.. code-block:: pycon
>>> lcm = LCM()
>>> lcm.clear()
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: General PSP functional error.
"""
with PSP() as psp:
self._open_device(psp)
i_ret = psp.lib.LMB_LCM_DisplayClear()
msg = get_psp_exc_msg("LMB_LCM_DisplayClear", i_ret)
if i_ret == ERR_Success:
logger.debug(f"clear string on LCM")
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
self._close_device(psp)
[docs]
def get_keys_status(self) -> int:
"""
Get LCM keys status.
bit 0 means Key 1, bit 1 means Key2, bit 2 means Key 3, bit 3 means Key4
1: pressed , 0: released
- 0 (0000): key1 -> off, key2 -> off, key3 -> off, key4 -> off
- 1 (0001): key1 -> on, key2 -> off, key3 -> off, key4 -> off
- 2 (0010): key1 -> off, key2 -> on, key3 -> off, key4 -> off
- 4 (0100): key1 -> off, key2 -> off, key3 -> on, key4 -> off
- 8 (1000): key1 -> off, key2 -> off, key3 -> off, key4 -> on
Example:
.. code-block:: pycon
>>> lcm = LCM()
>>> lcm.get_keys_status()
2
:return: LCM keys status
:rtype: int
:raises PSPNotOpened: The library is not ready or opened yet.
:raises PSPNotSupport: This function is not supported.
:raises PSPError: No key is pressed.
"""
ub_keys = c_uint8()
with PSP() as psp:
self._open_device(psp)
i_ret = psp.lib.LMB_LCM_KeysStatus(byref(ub_keys))
msg = get_psp_exc_msg("LMB_LCM_KeysStatus", i_ret)
if i_ret == ERR_Success:
logger.debug(f"LCM keys status is {ub_keys.value:02x}")
elif i_ret == ERR_NotOpened:
raise PSPNotOpened(msg)
elif i_ret == ERR_NotSupport:
raise PSPNotSupport(msg)
else:
raise PSPError(msg)
self._close_device(psp)
return ub_keys.value
@classmethod
def _callback(cls, stu_lcm_msg: LCMKeyMsg) -> None:
"""Callback function for :func:`exec_callback`."""
print(f"<Callback> LCM Item = 0x{stu_lcm_msg.ub_keys:02X}, "
f"Status = 0x{stu_lcm_msg.ub_status:02X}, "
f"time is {stu_lcm_msg.stu_time.uw_year:04d}/"
f"{stu_lcm_msg.stu_time.ub_month:02d}/"
f"{stu_lcm_msg.stu_time.ub_day:02d} "
f"{stu_lcm_msg.stu_time.ub_hour:02d}:"
f"{stu_lcm_msg.stu_time.ub_minute:02d}:"
f"{stu_lcm_msg.stu_time.ub_second:02d}")
[docs]
def exec_callback(self) -> None:
"""
Use callback function to detect LCM Keys status.
Example:
.. code-block:: pycon
>>> lcm = LCM()
>>> lcm.exec_callback()
----> hook LCM Keys Callback OK <----
===> pause !!! hit <enter> to end <===
<Callback> LCM Item = 0x08, Status = 0x08, time is 2022/08/02 16:48:28
<Callback> LCM Item = 0x08, Status = 0x00, time is 2022/08/02 16:48:28
<Callback> LCM Item = 0x04, Status = 0x04, time is 2022/08/02 16:48:29
<Callback> LCM Item = 0x04, Status = 0x00, time is 2022/08/02 16:48:29
<Callback> LCM Item = 0x08, Status = 0x08, time is 2022/08/02 16:48:30
<Callback> LCM Item = 0x08, Status = 0x00, time is 2022/08/02 16:48:30
<Callback> LCM Item = 0x02, Status = 0x02, time is 2022/08/02 16:48:31
<Callback> LCM Item = 0x02, Status = 0x00, time is 2022/08/02 16:48:31
<Callback> LCM Item = 0x01, Status = 0x01, time is 2022/08/02 16:48:31
<Callback> LCM Item = 0x01, Status = 0x00, time is 2022/08/02 16:48:31
<Callback> LCM Item = 0x08, Status = 0x08, time is 2022/08/02 16:48:32
<Callback> LCM Item = 0x08, Status = 0x00, time is 2022/08/02 16:48:32
----> hook LCM Keys Callback Disable OK <----
"""
c_callback = CFUNCTYPE(None, LCMKeyMsg)
p_callback = c_callback(self._callback)
with PSP() as psp:
self._open_device(psp)
i_ret = psp.lib.LMB_LCM_KeysCallback(p_callback, 150)
if i_ret != ERR_Success:
print("-----> hook LCM Keys callback failure <-------")
return
print("----> hook LCM Keys Callback OK <----")
print("===> pause !!! hit <enter> to end <===")
input()
i_ret = psp.lib.LMB_LCM_KeysCallback(None, 150)
if i_ret == ERR_Success:
print("----> hook LCM Keys Callback Disable OK <----")
self._close_device(psp)