Open nnako opened 1 year ago
heeey @nnako Thank you for your interest
Q: how do the different components (e.g. tmux, bash scripts, ...) work together to achieve the remote control? A: Tmux -> Freeplane
1.1. Tmux:
Tmux starts inside terminal emulator (xterm in my case, but it's doesn't matter) bash session
Tmux allows any new changes to a pane to be passed to a command. Then we can redirect the tmux pane as a stream into the named pipe file /tmp/tmux_pane.fifo
$ mkfifo /tmp/tmux_pane.fifo
$ tmux pipe-pane "cat > /tmp/tmux_pane.fifo"
As a result, we can run `$ tail -f /tmp/tmux_pane.fifo" and see the all changes in the tmux pane in "real-time".
1.2. Freeplane:
I wrote a small groovy script, this script starts in a separate java thread and continuously reads the named pipe /tmp/freeplane.fifo
file. The script assumes that all data in this named pipe file is raw groovy commands.
Before apply this script on the freeplane node, you need to create named pipe file /tmp/freeplane.fifo
$ mkfifo /tmp/freeplane.fifo
And here the script code
InputStream Fifo = new FileInputStream("/tmp/freeplane.fifo");
int buffer_size = 65536;
byte[] bytes = new byte[0x10000];
int bytes_read = -1;
while(1) {
while ((bytes_read = Fifo.read(bytes, 0, buffer_size)) == -1) {
// print "."
Thread.sleep(1000)
}
String text = new String(bytes, 0, bytes_read)
String groovy_script = """
import org.freeplane.plugin.script.proxy.ScriptUtils;
def c = ScriptUtils.c();
def node = ScriptUtils.node();
""" + text;
println groovy_script
EventQueue.invokeAndWait(() -> Eval.me(groovy_script))
}
At this point, you able to send the groovy code to freeplane instance through the write the groovy code to /tmp/freeplane.fifo
file.
$ echo 'node.createChild("hello github")' >> /tmp/freeplane.fifo
1.3. Text processing
Okay, we have two named pipe /tmp/tmux_pane.fifo
and /tmp/freeplane.fifo
files.
Let's create a small text processor for example, let's write parser for ifconfig
command. This parser will accept the output from ifconfig
command, and produce groovy code to stdout.
The following code is awk code.
/: flags/ {
IF_NAME=gensub(/:/, "", "g", $1)
FREEPLANE_IF_NAME=gensub(/\-/, "_", "g", IF_NAME)
printf("def %s = node.createChild(\"%s\");\n", FREEPLANE_IF_NAME, IF_NAME)
printf("def bmon_%s = %s.createChild(\"%s\");\n", FREEPLANE_IF_NAME, FREEPLANE_IF_NAME, "bmon")
printf("def iftop_%s = %s.createChild(\"%s\");\n", FREEPLANE_IF_NAME, FREEPLANE_IF_NAME, "iftop")
printf("bmon_%s.link.text = \"%s\";\n", FREEPLANE_IF_NAME, gensub(/ /, "%20", "g", "execute:_xterm -e ssh -t daria sudo bmon -p "IF_NAME))
printf("iftop_%s.link.text = \"%s\";\n", FREEPLANE_IF_NAME, gensub(/ /, "%20", "g", "execute:_xterm -e ssh -t daria sudo iftop -i "IF_NAME))
printf("%s.setFolded(true);\n", FREEPLANE_IF_NAME)
}
/[RT]X packets/ {
printf("%s[\"%s\"] = \"%s\";\n", FREEPLANE_IF_NAME, $1 , $3)
}
This simple awk scripts converts the following ifconfig vlan-public
output
vlan-public: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.25.255.223 netmask 255.255.0.0 broadcast 172.25.255.255
inet6 fe80::f66d:4ff:fe6f:f9fb prefixlen 64 scopeid 0x20<link>
ether f4:6d:04:6f:f9:fb txqueuelen 1000 (Ethernet)
RX packets 198892854 bytes 293609323848 (293.6 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 55235119 bytes 5329144404 (5.3 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
to this groovy code
def vlan_public = node.createChild("vlan-public");
def bmon_vlan_public = vlan_public.createChild("bmon");
def iftop_vlan_public = vlan_public.createChild("iftop");
bmon_vlan_public.link.text = "execute:_xterm%20-e%20ssh%20-t%20daria%20sudo%20bmon%20-p%20vlan-public";
iftop_vlan_public.link.text = "execute:_xterm%20-e%20ssh%20-t%20daria%20sudo%20iftop%20-i%20vlan-public";
vlan_public.setFolded(true);
vlan_public["RX"] = "198895464";
vlan_public["TX"] = "55236599";
Lastly, groovy code will be written to the /tmp/freeplane.fifo
and the groovy script from step 2 will execute it within the Freeplane Java context. That completes the process!
speaking very very roughly, it's working like this:
$ tail -f /tmp/tmux_pane.fifo | text_processing >> /tmp/freeplane.fifo
$ tmux send-keys -t ${TMUX_SESSION}.0 "${INPUT}" ENTER
$ tmux send-keys -t ${TMUX_SESSION}.0 "" ENTER
Just take a look to this groovy script:
node.link.text = 'execute:_xterm%20-e%20tmux%20attach%20-t%20' + node.id + '-Node'
node['script1'] = '''
node.children.each{ it.delete() }
output = FP.execNode("execNode.sh " + node.id + " '" + node.text + "'")
For example, bind this script execution on the some hot key (ctrl + e)
Then change node name in freeplane, for example to id
and press ctrl + e
combination.
The ${node.text}
will be passed as a second argument to bash script, that sends ${node.text}
as stdin in bash session and press "Enter" button.
And per each line that produced in stdout/stderr the id
($node.text}
) command, the following code will create new freeplane node.
if (output?.trim()) {
for( String values : output.split("\\n") ) {
node.createChild(values)
}
}
Q: within your introduction videos, there are different windows which pop up, but I don't see where they belong and how they interact. A: Yeah, randomly appearing windows can lead to confusion. Interaction between Mindwm components is based on a "message bus." For example, the Tmux pane generates three types of message events: bytestream, linestream, and IO-context stream. line-stream event:
{
"metadata": {
"tmux_pane_id": "%57", # unique tmux pane id
"fp_node_id": "0x10321312", # uniq freeplane node id
}
"message": {
"line": "hello world"
}
}
io-context event:
{
"metadata": {
"tmux_pane_id": "%403" # uniq tmux pane id
"fp_node_id": "0x32134132" # uniq freeplane node id
}
"io-context": {
"input": "uptime"
"output": "10:56:11 up 0:16, 1 user, load average: 0.28, 0.29, 0.21"
}
}
I use vector.dev (processing), kafka (message queue), opensearch (storage + API) and GRPC to process these events: The green color of the nodes indicates that they have been implemented and are ready to use in this repository.
Q: would it be possible to realize your concept within a Windows operating system? A: I believe it is possible. I package all of the necessary MindWM components into Kubernetes resources. Personally, I use minikube to deploy MindWM on my workstation. I hope the following diagram will explain the "multi-user" architecture:
Q: can the concept be used to implement control scripts (text files which contain control sequences) for controlling the Freeplane editor? A: Yes, it is very easy to achieve.
$ mkfifo /tmp/LOG
/tmp/example.groovy
file with following content:
node.createChild('test')
node.createChild('test1')
node.createChild('test2')
log.groovy
) on the root node.cat example.groovy | base64 >> /tmp/LOG
If I had some kind of structural concept description, the reasoning could be much easier. Please see the image below, where I have sketched my own library development freeplane-python-io to interface with Freeplane files. First of all, I really like the idea of freeplane-python-io. As I understand it, the main difference between our approaches is that MindWM run commands inside the running Freeplane instance, while the freeplane-python-io library uses raw XML to make the necessary changes to the mind map, correct?
Regarding your sketch, I hope my answers shed some light on the questions spots
Q: It might be possible to find Python libraries which realize parts of the remote mechanism you implemented?: A: It's a good question. I plan to write a Freeplane GRPC plugin. If I understand correctly, this would allow any GRPC client written in any language (such as Python, Ruby, or Lua) to interact with the mindmap objects within a running instance of Freeplane.
I am a power-user and script-developer within the Freeplane community.
Great, in the near future I plan to begin writing a Freeplane GRPC plugin, If you are interested in this, please join!
Apologies for any confusion. Although the issue was referenced in the pull request merge, it was actually invalid and should not have been closed. Therefore, the issue remains open.
Hi,
I am a power-user and script-developer within the Freeplane community. And I recently stumbled over your great concept research concerning a "remote control of the Freeplane editor". I really like the idea but I don't understand the tech stack you use, yet. My questions are:
And if that question was answered, I would spend some time to evaluate:
If I had some kind of structural concept description, the reasoning could be much easier. Please see the image below, where I have sketched my own library development freeplane-python-io to interface with Freeplane files. It might be possible to find Python libraries which realize parts of the remote mechanism you implemented?:
Within the image, I tried to put
question marks
where I don't know the implications, yet, regarding your remote control concept.If you have questions about interpreting the structure map, please feel free to get back to me. As everyone uses his own "language" for illustration of structure, it would be a miracle if somebody could get it by just looking at it ;-) .
Thanks.