Trajectory Planner using Ruckig Lib
10 Jun 2024 19:07 #302770
by Grotius
Replied by Grotius on topic Trajectory Planner using Ruckig Lib
Hi Guy's.
Nice discussion.
I finally found the G64 P & Q values for the trajectory planner.
The plan coming time is to integrate clodhoids into the linuxcnc interpreter.
The gcode name for clothoids is defined as G9.
So user can simply load his gcode file. We check if the file contains G64 params.
We then do G64 P&Q value optimalisation for entire gcode file, this is done offline.
Then we save the optimized gcode as a copy and load the copy into lcnc nml again.
The gcode is now reading the G9, the block is enchanged, checked, and now working on execute_block.
I will make notes on how to add stuff to the interpreter.
Earlyer today the repository is initialized from linuxcnc zero. You can now follow the commits i have done.
Nice discussion.
I finally found the G64 P & Q values for the trajectory planner.
The plan coming time is to integrate clodhoids into the linuxcnc interpreter.
The gcode name for clothoids is defined as G9.
So user can simply load his gcode file. We check if the file contains G64 params.
We then do G64 P&Q value optimalisation for entire gcode file, this is done offline.
Then we save the optimized gcode as a copy and load the copy into lcnc nml again.
The gcode is now reading the G9, the block is enchanged, checked, and now working on execute_block.
I will make notes on how to add stuff to the interpreter.
Earlyer today the repository is initialized from linuxcnc zero. You can now follow the commits i have done.
Please Log in or Create an account to join the conversation.
10 Jun 2024 19:11 #302771
by Becksvill
Replied by Becksvill on topic Trajectory Planner using Ruckig Lib
That's awesome Grotius
Looking forward to trying this out
Looking forward to trying this out
Please Log in or Create an account to join the conversation.
- ihavenofish
- Offline
- Platinum Member
Less
More
- Posts: 686
- Thank you received: 124
10 Jun 2024 19:34 #302773
by ihavenofish
Replied by ihavenofish on topic Trajectory Planner using Ruckig Lib
oooh
Please Log in or Create an account to join the conversation.
10 Jun 2024 20:09 #302775
by Lcvette
sweet! however i might give an example that is closer to how cam pumps out gcode. the G61/G64 often times is being swapped between. for example roughing strategies utilize G64 with larger P/Q values, while finishing operations will typically either use G61 or G64 with much smaller P/Q values. it is not uncommon for a code file to have several changes between G64 and G61 also with many changes to the G64 P/Q. Is the G9 going to specify only those section where G64 is located?
Replied by Lcvette on topic Trajectory Planner using Ruckig Lib
Hi Guy's.
Nice discussion.
I finally found the G64 P & Q values for the trajectory planner.
The plan coming time is to integrate clodhoids into the linuxcnc interpreter.
The gcode name for clothoids is defined as G9.
So user can simply load his gcode file. We check if the file contains G64 params.
We then do G64 P&Q value optimalisation for entire gcode file, this is done offline.
Then we save the optimized gcode as a copy and load the copy into lcnc nml again.
The gcode is now reading the G9, the block is enchanged, checked, and now working on execute_block.
I will make notes on how to add stuff to the interpreter.
Earlyer today the repository is initialized from linuxcnc zero. You can now follow the commits i have done.
sweet! however i might give an example that is closer to how cam pumps out gcode. the G61/G64 often times is being swapped between. for example roughing strategies utilize G64 with larger P/Q values, while finishing operations will typically either use G61 or G64 with much smaller P/Q values. it is not uncommon for a code file to have several changes between G64 and G61 also with many changes to the G64 P/Q. Is the G9 going to specify only those section where G64 is located?
The following user(s) said Thank You: spumco
Please Log in or Create an account to join the conversation.
10 Jun 2024 20:28 #302776
by PCW
Replied by PCW on topic Trajectory Planner using Ruckig Lib
I guess I should have said the tangential acceleration is currently just set by ini file constants.
but the tangential acceleration (acceleration in the line of motion)
is currently just set by an ini constant
That may not be correct. It certainly isn't for velocity. The ini file sets the velocity and acceleration for each joint. If a linear move is diagonal (where both X & Y axes contribute to the move), The overall velocity (and probably the accelleration) is higher becasue both axes are contributing to the move. I would expect the same applies to arcs where the motion at any point in time is tangential to the arc so resolving into X & Y forces will result in contribution by both X & Y axes which I would expect would achieve the same result as a linear move.
Please Log in or Create an account to join the conversation.
10 Jun 2024 20:46 #302777
by spumco
Good point.
Depending on the post, CAM posts I used for a couple different controllers (including LCNC) tends to use G61 for hole functions (drill/tap/ream), and a combo of G61/G64 with varying P/Q for roughing/finishing.
Replied by spumco on topic Trajectory Planner using Ruckig Lib
it is not uncommon for a code file to have several changes between G64 and G61 also with many changes to the G64 P/Q.
Good point.
Depending on the post, CAM posts I used for a couple different controllers (including LCNC) tends to use G61 for hole functions (drill/tap/ream), and a combo of G61/G64 with varying P/Q for roughing/finishing.
Please Log in or Create an account to join the conversation.
11 Jun 2024 10:56 #302807
by Grotius
Replied by Grotius on topic Trajectory Planner using Ruckig Lib
@Lcvette,
Is the G9 going to specify only those section where G64 is located?
For automation the G9 will be used to solve a gcode program using G64 P&Q values.
Then multiple G64 commands with different values in one gcode file are ok.
That's what is mentioned earlyer to deal with.
@All,
I have done a discovery. Was digging into how nurbs are processed. It turns out they are
loaded into the trajectory planner as a x number of arcs. So in fact the nurbs are transformed into
tiny arc segments.
I think it's better to interpolate nurbs, splines, etc by the trajectory planner itself.
This avoids tiny segments and is just more logical to do?
My plan is to certainly interpolate the tri-clothoids directly in the trajectory planner. So we add
a function : tpAddClothoids to this. And maybe also tpAddNurbs & tpAddCubicSpline etc.
It's also a idea to pass a general primitive like: tpAddItem(enum type); Where we just can load what
we want without messing with all kind of code.
I finally found how the interpreter rs274 is given the gcode to the realtime part of lcnc.
The interpreter is called by init(), read() etc. No problem.
Then the weird thing is, It calls function's that don't excists in the interpreter source itself.
That was the confusing part. I suspect you could name this callback functions.
The interpreter code will not compile without external program that has several interpreter functions declared.
For example, this function is not in the interpreter code :But it is called by the interpreter.
Then the milltask.so is the chain between the interpreter and the hal realtime part.
The hal realtime part is motmod.so, here it sends motion commands to the trajectory planner like : tpAddLine().. etc.
The interpreter has also a python interface, so the lcnc gui's can get the data to draw the opengl gremlin gcode.
Notes:
The plan now is to add a callback function to the interpreter for test :
void TRI_CLOTHOID(){}
This then has to be added into the source up to the trajectory planner. Let's try this.
Is the G9 going to specify only those section where G64 is located?
For automation the G9 will be used to solve a gcode program using G64 P&Q values.
Then multiple G64 commands with different values in one gcode file are ok.
That's what is mentioned earlyer to deal with.
@All,
I have done a discovery. Was digging into how nurbs are processed. It turns out they are
loaded into the trajectory planner as a x number of arcs. So in fact the nurbs are transformed into
tiny arc segments.
I think it's better to interpolate nurbs, splines, etc by the trajectory planner itself.
This avoids tiny segments and is just more logical to do?
My plan is to certainly interpolate the tri-clothoids directly in the trajectory planner. So we add
a function : tpAddClothoids to this. And maybe also tpAddNurbs & tpAddCubicSpline etc.
It's also a idea to pass a general primitive like: tpAddItem(enum type); Where we just can load what
we want without messing with all kind of code.
I finally found how the interpreter rs274 is given the gcode to the realtime part of lcnc.
The interpreter is called by init(), read() etc. No problem.
Then the weird thing is, It calls function's that don't excists in the interpreter source itself.
That was the confusing part. I suspect you could name this callback functions.
The interpreter code will not compile without external program that has several interpreter functions declared.
For example, this function is not in the interpreter code :
void STRAIGHT_TRAVERSE(){}
Then the milltask.so is the chain between the interpreter and the hal realtime part.
The hal realtime part is motmod.so, here it sends motion commands to the trajectory planner like : tpAddLine().. etc.
The interpreter has also a python interface, so the lcnc gui's can get the data to draw the opengl gremlin gcode.
Notes:
The plan now is to add a callback function to the interpreter for test :
void TRI_CLOTHOID(){}
This then has to be added into the source up to the trajectory planner. Let's try this.
Attachments:
Please Log in or Create an account to join the conversation.
11 Jun 2024 11:31 #302810
by rodw
Replied by rodw on topic Trajectory Planner using Ruckig Lib
Linuxcnc is supposed to be a generic interpreter that allows certain modules to be replaced so you can run different languages in addition to gcode..
Milltask is a replaceable task module loaded in the .ini file refer /src/emc/task folder. But it it is the only task module ever written.so it stands to reason that there are some specific functions in this module called by the system. What you called callbacks.
It was once suggested that there should be a plasmatask task module because plasma is so different to a milling machine...
Milltask is a replaceable task module loaded in the .ini file refer /src/emc/task folder. But it it is the only task module ever written.
TASK = milltask
It was once suggested that there should be a plasmatask task module because plasma is so different to a milling machine...
The following user(s) said Thank You: Grotius
Please Log in or Create an account to join the conversation.
11 Jun 2024 13:07 #302816
by akg1904
Replied by akg1904 on topic Trajectory Planner using Ruckig Lib
Hi,
Greetings to all.
I installed Grotius Repo and tried to run TpMod_Scurve in "axis_mm INI" but I keep getting error:
LINUXCNC - 2.10.0~pre0
Machine configuration directory is '/home/ethereal-abhi/linuxcnc/configs/sim/axis'
Machine configuration file is 'axis_mm.ini'
Starting LinuxCNC...
(time=1718094964.010793,pid=40867): Registering server on TCP port 5005.
(time=1718094964.011063,pid=40867): running server for TCP port 5005 (connection_socket = 3).
linuxcnc TPMOD=tpmod_scurve HOMEMOD=homemod EMCMOT=motmod_scurve
Note: Using POSIX realtime
tpmod_scurve: dlopen: /home/ethereal-abhi/linuxcnc/rtlib/tpmod_scurve.so: cannot open shared object file: No such file or directory
<commandline>:0: waitpid failed /home/ethereal-abhi/linuxcnc/bin/rtapi_app tpmod_scurve
<commandline>:0: /home/ethereal-abhi/linuxcnc/bin/rtapi_app exited without becoming ready
<commandline>:0: insmod for tpmod_scurve failed, returned -1
Note: Using POSIX realtime
task: machine: 'LinuxCNC-HAL-SIM-AXIS' version 'unknown'
Found file(lib): /home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal
motmod_scurve: dlopen: /home/ethereal-abhi/linuxcnc/rtlib/motmod_scurve.so: cannot open shared object file: No such file or directory
/home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal:7: waitpid failed /home/ethereal-abhi/linuxcnc/bin/rtapi_app motmod_scurve
/home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal:7: /home/ethereal-abhi/linuxcnc/bin/rtapi_app exited without becoming ready
/home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal:7: insmod for motmod_scurve failed, returned -1
Shutting down and cleaning up LinuxCNC...
/home/ethereal-abhi/linuxcnc/scripts/linuxcnc: line 668: /home/ethereal-abhi/linuxcnc/scripts/axis-remote: cannot execute: required file not found
(time=1718094964.421738,pid=40867): Deleting 3 channels from the NML_Main_Channel_List.
(time=1718094964.421764,pid=40867): Deleting emcCommand NML channel from NML_Main_Channel_List.
(time=1718094964.421785,pid=40867): deleting NML (1)
(time=1718094964.421801,pid=40867): delete (CMS *) 0x5597399a3300;
(time=1718094964.421829,pid=40867): rcs_shm_close(shm->key=1001(0x3E9),shm->size=8192(0x2000),shm->addr=0x7f246a56e000)
(time=1718094964.421859,pid=40867): deleting CMS (emcCommand)
(time=1718094964.421888,pid=40867): free( data = 0x5597399a4030);
(time=1718094964.421913,pid=40867): Leaving ~CMS()
(time=1718094964.421922,pid=40867): CMS::delete(0x5597399a3300)
(time=1718094964.421934,pid=40867): CMS::delete successful.
(time=1718094964.421967,pid=40867): Leaving ~NML()
(time=1718094964.421976,pid=40867): NML channel deleted from NML_Main_Channel_List
(time=1718094964.421986,pid=40867): Deleting emcStatus NML channel from NML_Main_Channel_List.
(time=1718094964.421993,pid=40867): deleting NML (2)
(time=1718094964.422001,pid=40867): delete (CMS *) 0x5597399a9510;
(time=1718094964.422011,pid=40867): rcs_shm_close(shm->key=1002(0x3EA),shm->size=20480(0x5000),shm->addr=0x7f246a569000)
(time=1718094964.422035,pid=40867): deleting CMS (emcStatus)
(time=1718094964.422043,pid=40867): free( data = 0x5597399aa220);
(time=1718094964.422054,pid=40867): Leaving ~CMS()
(time=1718094964.422064,pid=40867): CMS::delete(0x5597399a9510)
(time=1718094964.422073,pid=40867): CMS::delete successful.
(time=1718094964.422089,pid=40867): Leaving ~NML()
(time=1718094964.422096,pid=40867): NML channel deleted from NML_Main_Channel_List
(time=1718094964.422113,pid=40867): Deleting emcError NML channel from NML_Main_Channel_List.
(time=1718094964.422118,pid=40867): deleting NML (3)
(time=1718094964.422123,pid=40867): delete (CMS *) 0x5597399af900;
(time=1718094964.422144,pid=40867): rcs_shm_close(shm->key=1003(0x3EB),shm->size=8192(0x2000),shm->addr=0x7f246a567000)
(time=1718094964.422177,pid=40867): deleting CMS (emcError)
(time=1718094964.422188,pid=40867): free( data = 0x5597399b0620);
(time=1718094964.422198,pid=40867): Leaving ~CMS()
(time=1718094964.422204,pid=40867): CMS::delete(0x5597399af900)
(time=1718094964.422210,pid=40867): CMS::delete successful.
(time=1718094964.422217,pid=40867): Leaving ~NML()
(time=1718094964.422227,pid=40867): NML channel deleted from NML_Main_Channel_List
(time=1718094964.422240,pid=40867): deleting NML (1)
(time=1718094964.422248,pid=40867): Leaving ~NML()
(time=1718094964.422256,pid=40867): NML::operator delete(0x5597399a3090)
(time=1718094964.422263,pid=40867): NML channel deleted from Dynamically_Allocated_NML_Objects
(time=1718094964.422278,pid=40867): deleting NML (2)
(time=1718094964.422287,pid=40867): Leaving ~NML()
(time=1718094964.422293,pid=40867): NML::operator delete(0x5597399a9340)
(time=1718094964.422317,pid=40867): NML channel deleted from Dynamically_Allocated_NML_Objects
(time=1718094964.422326,pid=40867): deleting NML (3)
(time=1718094964.422339,pid=40867): Leaving ~NML()
(time=1718094964.422347,pid=40867): NML::operator delete(0x5597399af6d0)
(time=1718094964.422355,pid=40867): NML channel deleted from Dynamically_Allocated_NML_Objects
USRMOT: ERROR: command 31 timeout
emcTrajSetJoints(3) returned -2
emcMotionInit: emcTrajInit failed
Note: Using POSIX realtime
LinuxCNC terminated with an error. You can find more information in the log:
/home/ethereal-abhi/linuxcnc_debug.txt
and
/home/ethereal-abhi/linuxcnc_print.txt
as well as in the output of the shell command 'dmesg' and in the terminal
Can anyone help me out with this.
I really want to try this TP on my machine
Regards
Abhishek
Greetings to all.
I installed Grotius Repo and tried to run TpMod_Scurve in "axis_mm INI" but I keep getting error:
LINUXCNC - 2.10.0~pre0
Machine configuration directory is '/home/ethereal-abhi/linuxcnc/configs/sim/axis'
Machine configuration file is 'axis_mm.ini'
Starting LinuxCNC...
(time=1718094964.010793,pid=40867): Registering server on TCP port 5005.
(time=1718094964.011063,pid=40867): running server for TCP port 5005 (connection_socket = 3).
linuxcnc TPMOD=tpmod_scurve HOMEMOD=homemod EMCMOT=motmod_scurve
Note: Using POSIX realtime
tpmod_scurve: dlopen: /home/ethereal-abhi/linuxcnc/rtlib/tpmod_scurve.so: cannot open shared object file: No such file or directory
<commandline>:0: waitpid failed /home/ethereal-abhi/linuxcnc/bin/rtapi_app tpmod_scurve
<commandline>:0: /home/ethereal-abhi/linuxcnc/bin/rtapi_app exited without becoming ready
<commandline>:0: insmod for tpmod_scurve failed, returned -1
Note: Using POSIX realtime
task: machine: 'LinuxCNC-HAL-SIM-AXIS' version 'unknown'
Found file(lib): /home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal
motmod_scurve: dlopen: /home/ethereal-abhi/linuxcnc/rtlib/motmod_scurve.so: cannot open shared object file: No such file or directory
/home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal:7: waitpid failed /home/ethereal-abhi/linuxcnc/bin/rtapi_app motmod_scurve
/home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal:7: /home/ethereal-abhi/linuxcnc/bin/rtapi_app exited without becoming ready
/home/ethereal-abhi/linuxcnc/lib/hallib/core_sim.hal:7: insmod for motmod_scurve failed, returned -1
Shutting down and cleaning up LinuxCNC...
/home/ethereal-abhi/linuxcnc/scripts/linuxcnc: line 668: /home/ethereal-abhi/linuxcnc/scripts/axis-remote: cannot execute: required file not found
(time=1718094964.421738,pid=40867): Deleting 3 channels from the NML_Main_Channel_List.
(time=1718094964.421764,pid=40867): Deleting emcCommand NML channel from NML_Main_Channel_List.
(time=1718094964.421785,pid=40867): deleting NML (1)
(time=1718094964.421801,pid=40867): delete (CMS *) 0x5597399a3300;
(time=1718094964.421829,pid=40867): rcs_shm_close(shm->key=1001(0x3E9),shm->size=8192(0x2000),shm->addr=0x7f246a56e000)
(time=1718094964.421859,pid=40867): deleting CMS (emcCommand)
(time=1718094964.421888,pid=40867): free( data = 0x5597399a4030);
(time=1718094964.421913,pid=40867): Leaving ~CMS()
(time=1718094964.421922,pid=40867): CMS::delete(0x5597399a3300)
(time=1718094964.421934,pid=40867): CMS::delete successful.
(time=1718094964.421967,pid=40867): Leaving ~NML()
(time=1718094964.421976,pid=40867): NML channel deleted from NML_Main_Channel_List
(time=1718094964.421986,pid=40867): Deleting emcStatus NML channel from NML_Main_Channel_List.
(time=1718094964.421993,pid=40867): deleting NML (2)
(time=1718094964.422001,pid=40867): delete (CMS *) 0x5597399a9510;
(time=1718094964.422011,pid=40867): rcs_shm_close(shm->key=1002(0x3EA),shm->size=20480(0x5000),shm->addr=0x7f246a569000)
(time=1718094964.422035,pid=40867): deleting CMS (emcStatus)
(time=1718094964.422043,pid=40867): free( data = 0x5597399aa220);
(time=1718094964.422054,pid=40867): Leaving ~CMS()
(time=1718094964.422064,pid=40867): CMS::delete(0x5597399a9510)
(time=1718094964.422073,pid=40867): CMS::delete successful.
(time=1718094964.422089,pid=40867): Leaving ~NML()
(time=1718094964.422096,pid=40867): NML channel deleted from NML_Main_Channel_List
(time=1718094964.422113,pid=40867): Deleting emcError NML channel from NML_Main_Channel_List.
(time=1718094964.422118,pid=40867): deleting NML (3)
(time=1718094964.422123,pid=40867): delete (CMS *) 0x5597399af900;
(time=1718094964.422144,pid=40867): rcs_shm_close(shm->key=1003(0x3EB),shm->size=8192(0x2000),shm->addr=0x7f246a567000)
(time=1718094964.422177,pid=40867): deleting CMS (emcError)
(time=1718094964.422188,pid=40867): free( data = 0x5597399b0620);
(time=1718094964.422198,pid=40867): Leaving ~CMS()
(time=1718094964.422204,pid=40867): CMS::delete(0x5597399af900)
(time=1718094964.422210,pid=40867): CMS::delete successful.
(time=1718094964.422217,pid=40867): Leaving ~NML()
(time=1718094964.422227,pid=40867): NML channel deleted from NML_Main_Channel_List
(time=1718094964.422240,pid=40867): deleting NML (1)
(time=1718094964.422248,pid=40867): Leaving ~NML()
(time=1718094964.422256,pid=40867): NML::operator delete(0x5597399a3090)
(time=1718094964.422263,pid=40867): NML channel deleted from Dynamically_Allocated_NML_Objects
(time=1718094964.422278,pid=40867): deleting NML (2)
(time=1718094964.422287,pid=40867): Leaving ~NML()
(time=1718094964.422293,pid=40867): NML::operator delete(0x5597399a9340)
(time=1718094964.422317,pid=40867): NML channel deleted from Dynamically_Allocated_NML_Objects
(time=1718094964.422326,pid=40867): deleting NML (3)
(time=1718094964.422339,pid=40867): Leaving ~NML()
(time=1718094964.422347,pid=40867): NML::operator delete(0x5597399af6d0)
(time=1718094964.422355,pid=40867): NML channel deleted from Dynamically_Allocated_NML_Objects
USRMOT: ERROR: command 31 timeout
emcTrajSetJoints(3) returned -2
emcMotionInit: emcTrajInit failed
Note: Using POSIX realtime
LinuxCNC terminated with an error. You can find more information in the log:
/home/ethereal-abhi/linuxcnc_debug.txt
and
/home/ethereal-abhi/linuxcnc_print.txt
as well as in the output of the shell command 'dmesg' and in the terminal
Can anyone help me out with this.
I really want to try this TP on my machine
Regards
Abhishek
Please Log in or Create an account to join the conversation.
11 Jun 2024 13:30 #302818
by Grotius
Replied by Grotius on topic Trajectory Planner using Ruckig Lib
Hi Rod,
Milltask is compiled as executable. It's in the bin folder, the code has a main() function.
It does quite a lot.
- reading the interpreter rs274, turn them into canon commands, add them to a interp_list.
- is director of linuxcnc's program flow, is connected to nml server. (status channel, command channel)
- emcmot_command_t is the struct it uses to update the lcnc shared memory, the same is used by hal realtime motdot.so
Until now this is a valid G9 gcode :=9ptG9 X11 Y22 Z33 P0.1 Q0.2 R44.5 L321 E123.33 (L=integer type)
Where we could say G9 is a universal gcode where L stands for primitive-type.
Then primitive-type can be one of : line G0, line G1 , arc G2, arc G3, spline, nurb, clothoid etc, just a enum
that can accept all types of primitives. This would save us much coding work.
For a clothoid, we only need the endpoint xyz. The rest is done by the trajectory planner.
G9 L6 X10 Y10 Z0 (where L is the clothoid's enum)
G9 L0 X10 Y10 Z0 (where L is the line's G0 enum)
end so on.
Milltask is compiled as executable. It's in the bin folder, the code has a main() function.
It does quite a lot.
- reading the interpreter rs274, turn them into canon commands, add them to a interp_list.
- is director of linuxcnc's program flow, is connected to nml server. (status channel, command channel)
- emcmot_command_t is the struct it uses to update the lcnc shared memory, the same is used by hal realtime motdot.so
Until now this is a valid G9 gcode :=9ptG9 X11 Y22 Z33 P0.1 Q0.2 R44.5 L321 E123.33 (L=integer type)
Where we could say G9 is a universal gcode where L stands for primitive-type.
Then primitive-type can be one of : line G0, line G1 , arc G2, arc G3, spline, nurb, clothoid etc, just a enum
that can accept all types of primitives. This would save us much coding work.
For a clothoid, we only need the endpoint xyz. The rest is done by the trajectory planner.
G9 L6 X10 Y10 Z0 (where L is the clothoid's enum)
G9 L0 X10 Y10 Z0 (where L is the line's G0 enum)
end so on.
Please Log in or Create an account to join the conversation.
Time to create page: 0.192 seconds