secsgem
secsgem copied to clipboard
Clarification on how to use custom functions
Dear Benjamin, foremost, thank you for providing this library. I'd go nuts trying to de- and encode GEM communications otherwise.
However, I've run into troubles properly defining a S02F49 (Enhanced Remote Command) message in accordance to your documentation and the examples as seen, for example, in secs/functions/s06f08.py.
Ultimately, I am to send a message of the following format (from the equipment's manual):
S2F49 W
<L[4/1]
<U1[1/1] 12>
<A[7/1] "enhtest">
<A[7/1] "ADD-JOB">
<L[3/1]
<L[2/1]
<A[5/1] "JobID">
<A[5/1] "job50">
>
<L[2/1]
<A[10/1] "RecipeName">
<A[10/1] "transfer_1">
>
<L[2/1]
<A[9/1] "Transfers">
<L[25/1]
<U1[4/1] 1 1 2 1>
<U1[4/1] 2 4 4 2>
<U1[4/1] 1 2 3 4>
>
>
>
>
To achieve that, I've tried to define a custom message:
# SecsS02F49.py
from secsgem.secs.data_items import DataItemBase
from secsgem.secs.variables import Array
from secsgem.secs.functions import SecsStreamFunction
from secsgem.secs.data_items import DATAID, OBJSPEC, RCMD, CPNAME, CPVAL
class TRANSFERS(DataItemBase):
__type__ = Array
class SecsS02F49(SecsStreamFunction):
_stream = 2
_function = 49
_to_host: False
_to_equipment: True
_data_format = [
DATAID, # U1; purpose unknown
OBJSPEC, # A; "ENH" purpose unknown
RCMD, # A; static "ADD-JOB"
[
[
"JOBID", # name of the list
CPNAME, # A; static "JobID"
CPVAL # A; job_id
],
[
"RECIPE", # name of the list
CPNAME, # A; static "RecipeName"
CPVAL # A; recipe_name
],
# [
# CPNAME, # A; static "Transfers"
# TRANSFERS # CEPVAL "o00" (List of U1?)
# ]
]
]
and then to instatiate this class according to the examples I've seen earlier:
# main.py
import secsgem.gem
from SecsS02F49 import SecsS02F49
gem = secsgem.gem.GemHandler(
address="127.0.0.1",
port=5000,
active=True,
session_id=0,
name="test"
)
f = SecsS02F49({
"DATAID": 12,
"OBJSPEC": 'enhtest',
"RCMD": 'ADD-JOB',
"JOBID": [{"CPNAME": 'JobID', "CPVAL": 'job42'}],
"RECIPE": [{"CPNAME": 'RecipeName', "CPVAL": 'transfer_42'}]
})
print(f)
The above code yields a KeyError
at secs/variables/list_type.py:214, as the field_name
'JOBID' is being looked for in self.data
, which looks to me like a root object defined from my definition of _data_format:
> special variables
> function variables
> 'DATAID': <U1 12 >
> 'OBJSPEC': <A "enhtest">
> 'RCMD': <A "ADD-JOB">
> 'DATA': <L [2]
> len(): 4
What that ultimately tells me, is that I'm misunderstanding the examples I've seen, and that I'm either incorrectly defining _data_format, or I'm passing a malformed dictionary instantiating SecsS02F49 - or both.
To add to the confusion, if you remove the second list from _data_format:
# SecsS02F49.py
# ...
_data_format = [
DATAID, # U1; purpose unknown
OBJSPEC, # A; "ENH" purpose unknown
RCMD, # A; static "ADD-JOB"
[
[
"JOBID", # name of the list
CPNAME, # A; static "JobID"
CPVAL # A; job_id
],
# [
# "RECIPE", # name of the list
# CPNAME, # A; static "RecipeName"
# CPVAL # A; recipe_name
# ],
# [
# CPNAME, # A; static "Transfers"
# TRANSFERS # CEPVAL "o00" (List of U1?)
# ]
]
]
and consequently remove the corresponding line from the dictionary passed into the constructor:
# main.py
# ...
f = SecsS02F49({
"DATAID": 12,
"OBJSPEC": 'enhtest',
"RCMD": 'ADD-JOB',
"JOBID": [{"CPNAME": 'JobID', "CPVAL": 'job42'}],
# "RECIPE": [{"CPNAME": 'RecipeName', "CPVAL": 'transfer_42'}]
})
print(f)
The code runs just fine, resulting in the expected output:
S2F49
<L [4]
<U1 12 >
<A "enhtest">
<A "ADD-JOB">
<L [1]
<L [2]
<A "JobID">
<A "job42">
>
>
> .
I hope it'll be no trouble for you to spot what I'm dreadfully missing, and I'd be very grateful if you could point me towards a correct implementation.
Cheers from Germany, Alexander
P.S.: The described issue has been encountered with both the latest release version 0.1.0 and the latest development version (main branch of this repository as of today); the code above is attuned to the latter.
@alanchev Not sure if this fixes your problem, but the colons in this snippet is wrong. I guess you want to replace them by an equals sign.
class SecsS02F49(SecsStreamFunction):
_stream = 2
_function = 49
_to_host: False
_to_equipment: True
@thisch Now that you mention it, it is strikingly odd the runtime environment wouldn't raise an issue out of this syntax error. However, that was not the problem, the issue still resides in how to instantiate more complex objects. If I recall correctly, more fiddling surfaced that my issue most likely was about instantiating objects vs. lists incorrectly. Ultimately, the problem at hand got solved on a completely different path, and I pursued this issue no further. This issue might as well be closed or remain open for future discussion, in case anyone else stumbles across the same troubles.
Ultimately, the problem at hand got solved on a completely different path, and I pursued this issue no further.
Anyway, I've figured out how you can achieve what you wanted:
class SecsS02F49(SecsStreamFunction):
_stream = 2
_function = 49
_to_host = False
_to_equipment = True
_data_format = [
DATAID, # U1; purpose unknown
OBJSPEC, # A; "ENH" purpose unknown
RCMD, # A; static "ADD-JOB"
[
[
"JOBID", # name of the list
CPNAME, # A; static "JobID"
CPVAL # A; job_id
],
[
"RECIPE", # name of the list
CPNAME, # A; static "RecipeName"
CPVAL # A; recipe_name
],
# [
# CPNAME, # A; static "Transfers"
# TRANSFERS # CEPVAL "o00" (List of U1?)
# ]
]
]
print(SecsS02F49)
f = SecsS02F49({
"DATAID": 12,
"OBJSPEC": 'enhtest',
"RCMD": 'ADD-JOB',
"DATA": {
"JOBID": {"CPNAME": 'JobID', "CPVAL": 'job42'},
"RECIPE": {"CPNAME": 'RecipeName', "CPVAL": 'transfer_42'},
}
})
print(f.get())
Thank you, twmr! Unfortunately I will not be able to test your solution anytime soon, but it does look sensible. I hope others who need this question answered will come across your answer. ... Or, you know, some AI will learn from it and pass on the knowledge.
Either way, this issue will be closed now, as to not remain open forever.