Understanding free OPC/UA code in python - python

I am working on OPCUA in python. I am using freeopc. I have used their server_minimal & client_minimal example and it is running fine. I am having some issues understanding the code. As far as I know the OPCUA stack, it has address space which is like a collection of all the nodes. These nodes then further contains objects and these objects have variable from where we can read write data. Please correct me if I am wrong.
---------------------------------
Address space
---------------------------------
| |
| |
V V
Node1 Node2
|
Object1
|
Var1, Var2
So on the server side I want to know what is namespace
# setup our own namespace, not really necessary but should as spec
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)
What is the namespace used for.? What to put inside uri.?
On client side, I want to know:
After connecting to server, we are doing:
# Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
root = client.get_root_node()
print("Objects node is: ", root)
What does get_root_node() means. Is it like we are connecting to address space of server where all the nodes are defined.?
# Node objects have methods to read and write node attributes as well as browse or populate address space
print("Children of root are: ", root.get_children())
root.get_children()-- Does this means getting the objects of the nodes.?
# Now getting a variable node using its browse path
myvar = root.get_child(["0:Objects", "2:MyObject", "2:MyVariable"])
obj = root.get_child(["0:Objects", "2:MyObject"])
root.get_child what does it means.?
Client output:
('Objects node is: ', Node(TwoByteNodeId(i=84)))
('Children of root are: ', [Node(NumericNodeId(i=85)), Node(NumericNodeId(i=86)), Node(NumericNodeId(i=87))])
Above code is taken from server_minimal.py client_minimal.py
Can anyone please explain these. I tried reading their docs but this is not mentioned there.
Thanks.

I'm working with freeopcua too and on some questions I think I have an answer
root = client.get_root_node()
will get you the node of the root of your server so basically 'adress space' in your diagram.
root.get_children()
will return a list of all nodes that are direct children of the root so in the example of your tree.
[node1, node2]. However add the root node this is 0:Objects, 0:Types, 0:Views
To see the tree of the server you can best use the opcua-client this is a GUI that will allow you to see the tree.
to do this start your server and then in your terminal typ;
$ opcua-client
(when on linux)
You can add limitation to get children e.g.:
objects = root.get_children(refs=ua.ObjectIds.HasChild, nodeclassmask=ua.NodeClass.Object)
This will only return other objects not methods or properties of your node.
The output you get is because Node doesn't have a real 'ToString()' the i is the id of the node (can also be seen with the GUI client).
the
Node.getChild(NodeId)
will return a node object if you're certain you added a value you can get it,s value by calling .get_value() on the return of this. NodeId is the specification of what child you want. So say you want var1 this would be
# First the code needed to add the node
node1 = root.add_object(2, "Node1") # root is the root node which can be obtained by either client.get_root_node or server.get_root_node
object1 = node1.add_object(3, "Object1")
object1.add_variable(4, "Var1", 42)
object1.add_variable(4, "Var2", 13)
# Now the code to ask the server for the node
var1_node = root.getChild(["2:Node1", "3:Object1", "4:Var1"])
# And to get its value
var1_node.get_value()
Important here is that to get a child you need to know where you are (you can get children from any Node object not only root) and then go down with a combination of "idx:Name" which is what you add when you added the value to the server in the first place.
Hoped this helped a bit (did not test the code so it might need some adjustments to actually run)

To understand this better you can use, Unified Automation UA Expert as a client.
https://www.unified-automation.com/downloads/opc-ua-clients.html
Start the server_minimal.py, open UA Expert add server using 'Custom Discovery' with "opc.tcp://localhost:4840/freeopcua/server/". You can easily view the entire address space.

Related

Structure of MultibodyPlant, SceneGraph, Context, and Simulator for pydrake simulation

I'm getting very confused trying to setup my simulation correctly in PyDrake. What I want is to have an actuated robot (with e.g. an InverseDynamicsController on it) together with an object in the scene that the robot will manipulate. However, I'm struggling to sort out how to create and use the MultibodyPlant, SceneGraph, Context, Simulator combination correctly.
Here is roughly what I've tried to do:
builder = DiagramBuilder()
plant, scene_graph = AddMultibodyPlantSceneGraph(builder, time_step=1e-4)
parser = Parser(plant, scene_graph)
# Add my robot
robot = parser.AddModelFromFile(robot_urdf)
robot_base = plant.GetFrameByName('robot_base')
plant.WeldFrames(plant.world_frame(), robot_base)
# Add my object
parser.AddModelFromFile(FindResourceOrThrow("drake/my_object.urdf"))
plant.finalize()
# Add my controller
Kp = np.full(6, 100)
Ki = 2 * np.sqrt(Kp)
Kd = np.full(6, 1)
controller = builder.AddSystem(InverseDynamicsController(plant, Kp, Ki, Kd, False))
controller.set_name("sim_controller");
builder.Connect(plant.get_state_output_port(robot),
controller.get_input_port_estimated_state())
builder.Connect(controller.get_output_port_control(),
plant.get_actuation_input_port())
# Get the diagram, simulator, and contexts
diagram = builder.Build()
simulator = Simulator(diagram)
context = simulator.get_mutable_context()
plant_context = plant.GetMyContextFromRoot(context)
However, this has some undesirable qualities. First, as long as I've added the object, then I get this error:
Failure at systems/controllers/inverse_dynamics_controller.cc:32 in SetUp(): condition 'num_positions == dim' failed.
Second, with the object added, the object pose becomes part of my InverseKinematics problem, and when I do SetPositions with plant_context, I have to set both my arm joints AND the pose of the object, when I feel like I should only be setting the robot's joint positions with SetPositions.
I realize I've done something wrong with this setup, and I'm just wondering what is the correct way to have an instance of Simulator that I can run simulations with that has both an actuated robot, and a manipulable object? Am I supposed to create multiple plants? Multiple contexts? Who shares what with who?
I'd really appreciate some advice on this, or a pointer to an example. Drake is great, but I struggle to find minimal examples that do what I want.
Yes, you can add a separate MultibodyPlant for control. See https://github.com/RobotLocomotion/drake/blob/master/examples/planar_gripper/planar_gripper_simulation.cc for an example. The setup is similar to yours, though it's in C++. You can try mimicking the way the diagram is wired up there.
When you do have two plants, you want to call SetPositions on the simulation plant (not the control plant). You can set only the robot positions by using ModelInstanceIndex.
# Add my robot
robot = parser.AddModelFromFile(robot_urdf)
...
plant.SetPositions(plant_context, robot, robot_positions)

OpenTelemetry Python - How to instanciate a new span as a child span for a given trace_id

My goal is to perform tracing of the whole process of my application through several component. I am using GCP and Pub/Sub message queue to communicate information between components (developped in Python).
I am currently trying to keep the same root trace between component A and component B by creating a new span as a child span of my root trace.
Here is a small diagram:
Component A ---> Pub/Sub message ---> component B
(create the root trace) (contain information) (new span for root trace)
I have a given trace_id and span_id of my parent that I can transmit through Pub/Sub but I can't figure out how to declare a new span as a child of this last. All I managed to do is to link a new trace to the parent one but it is not the behavior I am looking for.
Has someone already tried to do something like that ?
Regards,
It's called trace context propagation and there are multiple formats such w3c trace context, jaeger, b3 etc... https://github.com/open-telemetry/opentelemetry-specification/blob/b46bcab5fb709381f1fd52096a19541370c7d1b3/specification/context/api-propagators.md#propagators-distribution. You will have to use one of the propagator's inject/extract methods for this. Here is the simple example using W3CTraceContext propagator.
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (BatchSpanProcessor,
ConsoleSpanExporter)
from opentelemetry.trace.propagation.tracecontext import \
TraceContextTextMapPropagator
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
tracer = trace.get_tracer(__name__)
prop = TraceContextTextMapPropagator()
carrier = {}
# Injecting the context into carrier and send it over
with tracer.start_as_current_span("first-span") as span:
prop.inject(carrier=carrier)
print("Carrier after injecting span context", carrier)
# Extracting the remote context from carrier and starting a new span under same trace.
ctx = prop.extract(carrier=carrier)
with tracer.start_as_current_span("next-span", context=ctx):
pass
For others coming here, this is what I ended up doing based on #dalalstreetboi3739's suggestions:
(I realized that the "trace_id" I got is already a w3c-conformant trace header https://www.w3.org/TR/trace-context/#trace-context-http-headers-format which allowed me to use the TraceContextTextMapPropagator directly ):
data = get_json()
trace_id_str = data['traceid']
carrier = {'traceparent': trace_id_str}
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
with tracer.start_as_current_span("/api/doit", ctx) as span:
# do awesome stuff here
# current span is now a child of the parent "trace_id"
requests.get("http://what")

How to create a synapse in NEURON?

How can I create a synapse in the NEURON simulator using its Python interface? I would like to create 2 Sections and connect them with a synapse, but there aren't any functions for it on the Section API or in the Section docs:
from neuron import h
src = h.Section(name="source")
dest = h.Section(name="destination")
In NEURON synapses are of the family of PointProcesses. You can insert a PointProcess into a Section by accessing it on the HocInterpreter and giving it a Segment on the Section. You then have to create a NetCon from a voltage pointer on the presynaptic Section (src(x)._ref_v) to the target point process:
from neuron import h
dest = h.Section(name="destination")
synapse = h.ExpSyn(sec(0.5))
src = h.Section(name="source")
connection = h.NetCon(src(0.5)._ref_v, synapse)
Make sure to keep references to each piece around or they get garbage collected. Also make sure that you set the proper connection.weight (it's an array, so usually connection.weight[0]) and that you set the proper attributes on the point process for it to function as well.

Get data at address from Cheat Engine with python

I've used Cheat Engine to get an Address and RealAddress for the X coordinate of my player in a game; Sea of Thieves. Unfortunately, the game crashes when I try to find the pointer by methods described online (ie clicking "Find out what access this address"). Since the Address and RealAddress don't seem to change (SoTGame.exe+699FE50 and 7FF6EA32FE50, respectively), is there a way to access the value at this address?
I'd like to use something like ReadWriteMemory (example code from the Git ReadMe w/my adaptations):
from ReadWriteMemory import ReadWriteMemory
rwm = ReadWriteMemory()
process = rwm.get_process_by_name('SoTGame.exe')
process.open()
#health_pointer= process.get_pointer(0x004e4dbc, offsets=[0xf4])
x_pointer = process.get_pointer(0x699FE50)
#health = process.read(health_pointer)
x = process.read(x_pointer)
print({'x': x})
Printing currently returns 0 while cheat engine is showing a very different number.
You want to access the address with offset to the module entry.
Calling get_process_by_name will return the pointer to the process base in memory, but here you want to offset this pointer to the module entry of the process.
The 0x699FE50 is your offset from the module entry, 'SoTGame.exe', hence finding and adding your module entry module_base (usually 0x00400000) will result in the correct address in memory.
In your case, get_pointer(module_base + 0x699FE50) should do the trick.

Using libsecret I can't get to the label of an unlocked item

I'm working on a little program that uses libsecret. This program should be able to create a Secret.Service...
from gi.repository import Secret
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
... get a specific Collection from that Service...
# 2 is the index of a custom collection I created, not the default one.
collection = service.get_collections()[2]
... and then list all the Items inside that Collection, this by just printing their labels.
# I'm just printing a single label here for testing, I'd need all of course.
print(collection.get_items()[0].get_label())
An important detail is that the Collecction may initially be locked, and so I need to include code that checks for that possibility, and tries to unlock the Collection.
# The unlock method returns a tuple (number_of_objs_unlocked, [list_of_objs])
collection = service.unlock_sync([collection])[1][0]
This is important because the code I currently have can do all I need when the Collection is initially unlocked. However if the Collection is initially locked, even after I unlock it, I can't get the labels from the Items inside. What I can do is disconnect() the Service, recreate the Service again, get the now unlocked Collection, and this way I am able to read the label on each Item. Another interesting detail is that, after the labels are read once, I no longer required the Service reconnection to access them. This seems quite inelegant, so I started looking for a different solution.
I realized that the Collection inherited from Gio.DBusProxy and this class caches the data from the object it accesses. So I'm assuming that is the problem for me, I'm not updating the cache. This is strange though because the documentation states that Gio.DBusProxy should be able to detect changes on the original object, but that's not happening.
Now I don't know how to update the cache on that class. I've taken a look at some seahorse(another application that uses libsecret) vala code, which I wasn't able to completely decipher, I can't code vala, but that mentioned the Object.emit() method, I'm still not sure how I could use that method to achieve my goal. From the documentation(https://lazka.github.io/pgi-docs/Secret-1/#) I found another promising method, Object.notify(), which seems to be able to send notifications of changes that would enable cache updates, but I also haven't been able to properly use it yet.
I also posted on the gnome-keyring mailing list about this...
https://mail.gnome.org/archives/gnome-keyring-list/2015-November/msg00000.html
... with no answer so far, and found a bugzilla report on gnome.org that mentions this issue...
https://bugzilla.gnome.org/show_bug.cgi?id=747359
... with no solution so far(7 months) either.
So if someone could shine some light on this problem that would be great. Otherwise some inelegant code will unfortunately find it's way into my little program.
Edit-0:
Here is some code to replicate the issue in Python3.
This snippet creates a collection 'test_col', with one item 'test_item', and locks the collection. Note libsecret will prompt you for the password you want for this new collection:
#!/usr/bin/env python3
from gi import require_version
require_version('Secret', '1')
from gi.repository import Secret
# Create schema
args = ['com.idlecore.test.schema']
args += [Secret.SchemaFlags.NONE]
args += [{'service': Secret.SchemaAttributeType.STRING,
'username': Secret.SchemaAttributeType.STRING}]
schema = Secret.Schema.new(*args)
# Create 'test_col' collection
flags = Secret.CollectionCreateFlags.COLLECTION_CREATE_NONE
collection = Secret.Collection.create_sync(None, 'test_col', None, flags, None)
# Create item 'test_item' inside collection 'test_col'
attributes = {'service': 'stackoverflow', 'username': 'xor'}
password = 'password123'
value = Secret.Value(password, len(password), 'text/plain')
flags = Secret.ItemCreateFlags.NONE
Secret.Item.create_sync(collection, schema, attributes,
'test_item', value, flags, None)
# Lock collection
service = collection.get_service()
service.lock_sync([collection])
Then we need to restart the gnome-keyring-daemon, you can just logout and back in or use the command line:
gnome-keyrin-daemon --replace
This will setup your keyring so we can try opening a collection that is initially locked. We can do that with this code snippet. Note that you will be prompted once again for the password you set previously:
#!/usr/bin/env python3
from gi import require_version
require_version('Secret', '1')
from gi.repository import Secret
# Get the service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Find the correct collection
for c in service.get_collections():
if c.get_label() == 'test_col':
collection = c
break
# Unlock the collection and show the item label, note that it's empty.
collection = service.unlock_sync([collection])[1][0]
print('Item Label:', collection.get_items()[0].get_label())
# Do the same thing again, and it works.
# It's necessary to disconnect the service to clear the cache,
# Otherwise we keep getting the same empty label.
service.disconnect()
# Get the service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Find the correct collection
for c in service.get_collections():
if c.get_label() == 'test_col':
collection = c
break
# No need to unlock again, just show the item label
print('Item Label:', collection.get_items()[0].get_label())
This code attempts to read the item label twice. One the normal way, which fails, you should see an empty string, and then using a workaround, that disconnects the service and reconnects again.
I came across this question while trying to update a script I use to retrieve passwords from my desktop on my laptop, and vice versa.
The clue was in the documentation for Secret.ServiceFlagsā€”there are two:
OPEN_SESSION = 2
establish a session for transfer of secrets while initializing the Secret.Service
LOAD_COLLECTIONS = 4
load collections while initializing the Secret.Service
I think for a Service that both loads collections and allows transfer of secrets (including item labels) from those collections, we need to use both flags.
The following code (similar to your mailing list post, but without a temporary collection set up for debugging) seems to work. It gives me the label of an item:
from gi.repository import Secret
service = Secret.Service.get_sync(Secret.ServiceFlags.OPEN_SESSION |
Secret.ServiceFlags.LOAD_COLLECTIONS)
collections = service.get_collections()
unlocked_collection = service.unlock_sync([collections[0]], None)[1][0]
unlocked_collection.get_items()[0].get_label()
I have been doing this
print(collection.get_locked())
if collection.get_locked():
service.unlock_sync(collection)
Don't know if it is going to work though because I have never hit a case where I have something that is locked. If you have a piece of sample code where I can create a locked instance of a collection then maybe I can help

Categories