- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1
Add OPC UA interface, device, init, and states #184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from ..lewis_versions import LEWIS_LATEST | ||
| from .device import SimulatedFinsPLC | ||
|  | ||
| framework_version = LEWIS_LATEST | ||
| __all__ = ["SimulatedFinsPLC"] | ||
| Original file line number | Diff line number | Diff line change | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | |||||||||||||||||
| from collections import OrderedDict | |||||||||||||||||
|  | |||||||||||||||||
| from lewis.devices import StateMachineDevice | |||||||||||||||||
| from lewis.core.statemachine import State | |||||||||||||||||
| Check noticeCode scanning / CodeQL Unused import Note 
      Import of 'State' is not used.
               Copilot AutofixAI 5 months ago To fix the problem, we will remove the unused import statement  
  Suggested changeset
  1
 
      
    lewis_emulators/opcua/device.py
     
 
 
                  Copilot is powered by AI and may make mistakes. Always verify output.
                 Positive FeedbackNegative Feedback 
                  
                  Refresh and try again.
                 | |||||||||||||||||
|  | |||||||||||||||||
| from .states import DefaultState | |||||||||||||||||
| Check failure on line 6 in lewis_emulators/opcua/device.py 
     | |||||||||||||||||
| Check noticeCode scanning / CodeQL Unused import Note 
      Import of 'DefaultState' is not used.
               Copilot AutofixAI 5 months ago To fix the problem, we will remove the unused import statement  
  Suggested changeset
  1
 
      
    lewis_emulators/opcua/device.py
     
 
 
                  Copilot is powered by AI and may make mistakes. Always verify output.
                 Positive FeedbackNegative Feedback 
                  
                  Refresh and try again.
                 | |||||||||||||||||
|  | |||||||||||||||||
| class SimulatedOPCUA(StateMachineDevice): | |||||||||||||||||
| """Class representing a simulated OPC UA PLC. | |||||||||||||||||
| """ | |||||||||||||||||
|  | |||||||||||||||||
| #Need a dictionary ? of some sort to store PV values and their | |||||||||||||||||
| #corresponding address/name on the OPCUA server. Also need | |||||||||||||||||
| #IP address and other server info, and possibly library to start | |||||||||||||||||
| #the server... | |||||||||||||||||
|  | |||||||||||||||||
| def _get_state_handlers(self): | |||||||||||||||||
| return super()._get_state_handlers() | |||||||||||||||||
|  | |||||||||||||||||
| def _get_initial_state(self): | |||||||||||||||||
| return super()._get_initial_state() | |||||||||||||||||
|  | |||||||||||||||||
| def _get_transition_handlers(self): | |||||||||||||||||
| return super()._get_transition_handlers() | |||||||||||||||||
| Original file line number | Diff line number | Diff line change | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | |||||||||||
| from lewis.adapters import opcua | |||||||||||
| Check noticeCode scanning / CodeQL Unused import Note 
      Import of 'opcua' is not used.
               Copilot AutofixAI 5 months ago To fix the issue, the unused import statement  
  Suggested changeset
  1
 
      
    lewis_emulators/opcua/interfaces/opcua_interface.py
     
 
                  Copilot is powered by AI and may make mistakes. Always verify output.
                 Positive FeedbackNegative Feedback 
                  
                  Refresh and try again.
                 | |||||||||||
|  | |||||||||||
| """ | |||||||||||
| This is a fake OPC UA server interface which creates three values: | |||||||||||
| a temperature float, a name string, and a status boolean. | |||||||||||
| """ | |||||||||||
|  | |||||||||||
| class OPCUAInterface: | |||||||||||
| """Interface for my device.""" | |||||||||||
|  | |||||||||||
| def __init__(self): | |||||||||||
| self._temperature = 25.0 | |||||||||||
| self._vacuum_status = False | |||||||||||
|  | |||||||||||
| @property | |||||||||||
| def temperature(self): | |||||||||||
| """Current temperature (read/write)""" | |||||||||||
| return self._temperature | |||||||||||
|  | |||||||||||
| @temperature.setter | |||||||||||
| def set_temperature(self, value): | |||||||||||
| """Set the temperature.""" | |||||||||||
| self.temperature = float(value) | |||||||||||
|  | |||||||||||
| @property | |||||||||||
| def vacuum_status(self): | |||||||||||
| """Vacuum status (read-only)""" | |||||||||||
| return self._vacuum_status | |||||||||||
|  | |||||||||||
| def set_vacuum(self, status: bool): | |||||||||||
| """Simulate a change in vacuum status""" | |||||||||||
| self._vacuum_status = bool(status) | |||||||||||
|  | |||||||||||
| @property | |||||||||||
| def name(self): | |||||||||||
| """Device name (read-only)""" | |||||||||||
| return "TestOPCUADevice" | |||||||||||
|  | |||||||||||
|  | |||||||||||
|  | |||||||||||
| def reset(self): | |||||||||||
| """Reset the device.""" | |||||||||||
| self.temperature = 25.0 | |||||||||||
| self._vacuum_status = False | |||||||||||
| return True | |||||||||||
|  | |||||||||||
| # Configuration for the adapter | |||||||||||
| opcua_adapter = { | |||||||||||
| 'options': { | |||||||||||
| 'port': 4840, | |||||||||||
| 'server_name': 'OPC UA Server', | |||||||||||
| 'read_only_properties': ['status'] | |||||||||||
| } | |||||||||||
| } | |||||||||||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from lewis.core.statemachine import State | ||
|  | ||
|  | ||
| class DefaultState(State): | ||
| pass | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import asyncio | ||
| import logging | ||
|  | ||
| from asyncua import Server, ua | ||
| from asyncua.common.methods import uamethod | ||
|  | ||
|  | ||
| @uamethod | ||
| def func(parent, value): | ||
| return value * 2 | ||
|  | ||
|  | ||
| async def main(): | ||
| _logger = logging.getLogger(__name__) | ||
| # setup our server | ||
| server = Server() | ||
| await server.init() | ||
| server.set_endpoint("opc.tcp://0.0.0.0:4840") | ||
|  | ||
| # set up our own namespace, not really necessary but should as spec | ||
| uri = "http://isiscomputing-namespace.epics" | ||
| idx = await server.register_namespace(uri) | ||
|  | ||
| # populating our address space | ||
| # server.nodes, contains links to very common nodes like objects and root | ||
| print(type(idx), idx) | ||
| myobj = await server.nodes.objects.add_object(idx, bname="Pressure_Values") | ||
| pressure_var = await myobj.add_variable(idx, "Pressure", 6.7) | ||
| temp_var = await myobj.add_variable(idx, "Temperature", 30.5) | ||
| # Set Pressure and Temperature to be writable by clients | ||
| await pressure_var.set_writable() | ||
| await temp_var.set_writable() | ||
| await server.nodes.objects.add_method( | ||
| ua.NodeId("ServerMethod", idx), | ||
| ua.QualifiedName("ServerMethod", idx), | ||
| func, | ||
| [ua.VariantType.Int64], | ||
| [ua.VariantType.Int64], | ||
| ) | ||
| _logger.info("Starting server!") | ||
|  | ||
| print(f"Pressure NodeId: {pressure_var.nodeid}") | ||
| print(f"Temperature NodeId: {temp_var.nodeid}") | ||
| async with server: | ||
| while True: | ||
| await asyncio.sleep(1) | ||
| new_val = await pressure_var.get_value() + 0.1 | ||
| new_val2 = await temp_var.get_value() + 0.05 | ||
| _logger.info("Set value of %s to %.1f", pressure_var, new_val) | ||
| _logger.info("Set value of %s to %.1f", temp_var, new_val2) | ||
| await pressure_var.write_value(new_val) | ||
| await temp_var.write_value(new_val2) | ||
|  | ||
|  | ||
| if __name__ == "__main__": | ||
| logging.basicConfig(level=logging.DEBUG) | ||
| asyncio.run(main(), debug=False) | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -1 +1,2 @@ | ||
| lewis | ||
| asyncua | 
Check notice
Code scanning / CodeQL
Unused global variable Note
Copilot Autofix
AI 5 months ago
To fix the issue, we need to either:
_unused_framework_version), orSince there is no indication that the variable is intentionally unused or required for documentation purposes, the best approach is to remove the assignment entirely. This ensures the code remains clean and avoids confusion about the purpose of the variable.