I am trying to use Graphviz to draw a TCP/IP data encapsulation diagram in Stevens' classical TCP/IP book.
The diagram seems not a standard computer graph. My questions are:
how to align to right?
how to mark the size below each record(i.e. 14 in IP header, 20 in TCP)?
how to draw a edge like |<----------- 46 to 1500 bytes ---------->| and put it below some node?
Can graphviz draw it? If so, how? Thanks in advance.
For alignment, I used straight edges (splines=false) and an indication of which corner of the node they come from and which corner of the other node they enter (compass points).
To create labels under the words, I used a HTML-label with a two-row table, in the second row I made the bottom borders and the borders on the left and right sides invisible by setting the attribute sides="T" (T meaning top border).
For drawing edge like |<----------- 46 to 1500 bytes ---------->|, I used node's width reduction (width=.01) and invisible nodes and edges, you can find them in the script below by attribute style=invis. Here is a small example with red tinted elements that are invisible in the final image:
Graphviz can draw it (see below for a example), but as answered in the comments above, it will be faster to use graphics editor.
Script:
digraph TCP_IP_diagram {
label=<<FONT><B>Figure 1.7 </B> Encapsulation of data as it goes down the protocol stack.</FONT>>
nodesep=0
ranksep=.1
splines=false
edge[style=dashed]
node[shape=box height=.6]
// NODES ------------------------------------
table_0 [margin=0 shape=none label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="f0" HEIGHT="40" FIXEDSIZE="TRUE">user data</TD></TR>
</TABLE>>]
inv_0 [shape=point style=invis]
table_1 [margin=0 shape=none label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR>
<TD PORT="f0">Appl<BR/>header</TD>
<TD PORT="f1">user data</TD>
</TR>
</TABLE>>]
inv_00 [shape=point style=invis]
table_2 [margin=0 shape=none label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR>
<TD PORT="f0">TCP<BR/>header</TD>
<TD PORT="f1"> application data</TD>
</TR>
</TABLE>>]
{
rank=same;
"TCP segment" [shape=plaintext]
inv_1 [shape=point height=.01]
inv_2 [shape=point height=.01]
}
table_3 [margin=0 shape=none label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR>
<TD PORT="f0">IP<BR/>header</TD>
<TD>TCP<BR/>header</TD>
<TD PORT="f1"> application data</TD>
</TR>
</TABLE>>]
{
rank=same;
"IP datagram" [shape=plaintext]
inv_3 [shape=point height=.01]
inv_4 [shape=point height=.01]
}
table_4 [margin=0 shape=none label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR>
<TD PORT="f0">Ethernet<BR/>header</TD>
<TD>IP<BR/>header</TD>
<TD>TCP<BR/>header</TD>
<TD> application data</TD>
<TD PORT="f1">Ethernet<BR/>trailer</TD>
</TR>
<TR>
<TD SIDES="T">14</TD>
<TD SIDES="T">20</TD>
<TD SIDES="T">20</TD>
<TD SIDES="T"></TD>
<TD SIDES="T">4</TD>
</TR>
</TABLE>>]
{
rank=same;
"Ethernet frame" [shape=plaintext]
inv_5 [shape=box height=.4 width=.01 label=""]
inv_6 [shape=box height=.4 width=.01 label=""]
}
{
rank=same;
"46 to 1500 bytes" [shape=plaintext]
inv_7 [shape=box height=.4 width=.01 label=""]
inv_8 [shape=box height=.4 width=.01 label=""]
}
table_5 [margin=0 shape=none label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BORDER="4" PORT="f0" SIDES="T">Ethernet</TD></TR>
</TABLE>>]
morespace [style=invis shape=plain]
// EDGES ------------------------------------
table_0:f0:se -> table_1:f1:ne
table_0:f0:sw -> table_1:f1:nw
table_0 -> inv_0 [style=invis]
inv_0 -> table_1 [style=invis]
table_1:f0:sw -> table_2:f0:ne
table_1:f1:se -> table_2:f1:ne
table_1 -> inv_00 [style=invis]
inv_00 -> table_2 [style=invis]
table_2 -> "TCP segment" [style=invis]
table_2:f0:sw -> inv_1 [arrowhead=none]
table_2:f1:se -> inv_2 [arrowhead=none]
inv_1 -> table_3:f0:ne
inv_2 -> table_3:f1:ne
"TCP segment" -> inv_1 [weight=0 style=solid]
"TCP segment" -> inv_2 [weight=0 style=solid]
table_3 -> "IP datagram" [style=invis]
table_3:f0:sw -> inv_3 [arrowhead=none]
table_3:f1:se -> inv_4 [arrowhead=none]
"IP datagram" -> inv_3 [weight=0 style=solid]
"IP datagram" -> inv_4 [weight=0 style=solid]
inv_3 -> table_4:f0:ne [style=dashed]
inv_4 -> table_4:f1:nw
table_4 -> "Ethernet frame" [style=invis]
table_4:f0:sw -> inv_5 [style=invis]
table_4:f1:se -> inv_6 [style=invis]
"Ethernet frame" -> inv_5 [weight=0 style=solid]
"Ethernet frame" -> inv_6 [weight=0 style=solid]
"Ethernet frame" -> "46 to 1500 bytes" [style=invis]
table_4:f0:se -> inv_7 [style=invis]
table_4:f1:sw -> inv_8 [style=invis]
"46 to 1500 bytes" -> inv_7 [weight=0 style=solid]
"46 to 1500 bytes" -> inv_8 [weight=0 style=solid]
"46 to 1500 bytes" -> morespace [style=invis]
{
edge [minlen=2]
"application" -> "TCP" -> "IP" -> "Ethernet\ndriver"
}
"Ethernet\ndriver" -> table_5:f0 [style=solid arrowhead=none]
// OTHER RANKS ------------------------------------
{rank=same;"application";inv_0}
{rank=same;table_4;table_5}
}
Result:
Related
I want to generate UTF-8 HTML output from a data frame using kable. I know that there are many similar questions on stackoverflow, but I still can't find a solution to this problem.
kable("ب",format="html")
generates:
<table>
<thead>
<tr>
<th style="text-align:left;"> x </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"> <U+0628> </td>
</tr>
</tbody>
</table>
R is running on Windows with the following session info:
> sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)
Matrix products: default
locale:
[1] LC_COLLATE=English_Canada.1252 LC_CTYPE=English_Canada.1252
[3] LC_MONETARY=English_Canada.1252 LC_NUMERIC=C
[5] LC_TIME=English_Canada.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] knitr_1.38
loaded via a namespace (and not attached):
[1] compiler_4.0.3 tools_4.0.3 highr_0.8 xfun_0.30
and syslocale:
Sys.getlocale()
[1] "LC_COLLATE=English_Canada.1252;LC_CTYPE=English_Canada.1252;LC_MONETARY=English_Canada.1252;LC_NUMERIC=C;LC_TIME=English_Canada.1252"
I've tried setting my locale to "en_US.UTF-8" but it seems this isn't supported on Windows. I also tried Sys.setlocale("LC_CTYPE", "arabic") but it didn't help.
I know how to convert the text in the table to html utf-8 escape codes (like &#xxxx;) but this makes for an awkward html file.
Is there a good solution for this? Or is it better to use a non-windows system for working with UTF-8?
I found a possible solution by searching and replacing escaped utf-8 codes, but I'm hoping for a better solution.
library(knitr)
library(dplyr)
rep_utf8 = function(x){
rep_one = function(x){
pattern = "<U\\+([A-F0-9]+)>"
a1 = regexpr(pattern,x,perl=TRUE)
if(a1==-1) return(NULL)
a2 = attr(a1,"match.length")
a3 = attr(a1,"capture.start")
a4 = attr(a1,"capture.length")
j = strtoi(substr(x,a3,a3+a4-1),base=16L)
a = intToUtf8(as.integer(j))
paste0(
substr(x,0,a1-1),
a,
substr(x,a1+a2,nchar(x))
)
}
while(TRUE){
x2 = rep_one(x)
if(is.null(x2)) break;
x = x2
}
x
}
writeUtf8 = function(a,filename){
con = file(filename,"wb")
writeBin(charToRaw(a), con)
close(con)
}
a = "ب"
kable(a,format="html") %>%
rep_utf8() %>%
writeUtf8("test.html")
generates:
<table>
<thead>
<tr>
<th style="text-align:left;"> x </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"> ب </td>
</tr>
</tbody>
</table>
An uncaught Exception was encountered
Type: Mpdf\MpdfException
Message: must precede in a table
Filename: /home/press.altisinfonet.com/public_html/vendor/mpdf/mpdf/src/Mpdf.php
Line Number: 13410
Backtrace:
File: /home/press.altisinfonet.com/public_html/vendor/mpdf/mpdf/src/Mpdf.php
Line: 23545
Function: TableHeaderFooter
File: /home/press.altisinfonet.com/public_html/vendor/mpdf/mpdf/src/Tag/Table.php
Line: 1121
Function: _tableWrite
File: /home/press.altisinfonet.com/public_html/vendor/mpdf/mpdf/src/Tag.php
Line: 246
Function: close
File: /home/press.altisinfonet.com/public_html/vendor/mpdf/mpdf/src/Mpdf.php
Line: 15272
Function: CloseTag
File: /home/press.altisinfonet.com/public_html/app/libraries/Tec_mpdf.php
Line: 68
Function: WriteHTML
File: /home/press.altisinfonet.com/public_html/app/libraries/Sma.php
Line: 302
Function: generate
File: /home/press.altisinfonet.com/public_html/app/controllers/admin/Sales.php
Line: 418
Function: generate_pdf
File: /home/press.altisinfonet.com/public_html/index.php
Line: 303
Function: require_once
In newer mPDF versions (or in a plaintext rendering), the message of this exception is <tfoot> must precede <tbody> in a table.
By HTML 4 specification, the order of table elements has to be <thead> → <tfoot> → <tbody> (this directly contradicts HTML 5 specification where the order is <thead> → <tbody> → <tfoot>).
mPDF adheres to the HTML 4 in this matter.
Put yout <tbody> to the end of the table and the exception will not be thrown.
See also: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot
In eXist-DB 4.4 I have managed to deploy a simple Lucern query with KWIC output as a table.
I have a collection of tei:xml documents which looks like this sample:
<TEI xml:id="MS609-0001.xml">
<text xml:id="MS609-0001">
[...]
<seg type="dep_event" subtype="event" xml:id="MS609-0001-1">
<pb n="1r"/>
<lb break="n" n="1"/>
<date type="deposition_date" when="1245-05-27" cert="high">Anno
Domini M° CC° XL° quinto VI Kalendas Iunii.</date>
<persName nymRef="#Arnald_Garnier_MSP-AU" role="dep">Arnaldus Garnerii</persName>
testis iuratus dixit quod vidit in
<placeName type="event_loc" nymRef="#home_of_Cap-de-Porc">domo
<persName nymRef="#Peire_Cap-de-Porc_MSP-AU" role="own">Petri de Sancto Andrea</persName>
</placeName>
<lb break="y" n="2"/>
<persName nymRef="#Bernard_Cap-de-Porc_MSP-AU" role="her">B<supplied reason="expname">ernardum</supplied> de Sancto Andrea</persName>,
fratrem dicti Petri, et socium eius, hereticos. Et vidit ibi cum eis dictum
<persName nymRef="#Peire_Cap-de-Porc_MSP-AU" ana="#uAdo" role="par">P<supplied reason="expname">etrum</supplied> de Sancto Andrea</persName> et
<persName nymRef="#Susanna_Cap-de-Porc_MSP-AU" ana="#uAdo" role="par">uxor dicti<lb break="y" n="3"/>Petri</persName>. Et
<persName nymRef="#Arnald_Garnier_MSP-AU" ana="#pAdo" role="par"/>ipse
testis adoravit ibi dictos hereticos, sed non vidit alios adorare. Et
<date type="event_date" when="1239">sunt VI anni vel circa</date>.
<seg type="inq_int" subtype="specific_question">Et quando ipse testis exivit<lb break="y" n="4"/>domum invenit
<persName nymRef="#Guilhem_de_Rosengue_MSP-AU" key="inqint" ana="#pIntra" role="ref">Willelmus de Rozergue</persName> intrantem ad dictos hereticos.</seg>
</seg>
<seg>
[...]
</seg>
[...]
<text>
<TEI>
With this function calling KWIC:
xquery version "3.1";
declare namespace tei="http://www.tei-c.org/ns/1.0";
import module namespace kwic="http://exist-db.org/xquery/kwic";
let $query :=
<query>
<wildcard>heret*</wildcard>
</query>
for $hit in collection('/db/apps/deheresi/data/')//tei:seg[ft:query(.,$query)]
order by ft:score($hit) descending
return
kwic:summarize($hit, <config width="80" table="yes" />)
I get for example these results as a table:
<tr>
<td class="previous">...ernardum de Sancto Andrea,
fratrem dicti Petri, et socium eius, </td>
<td class="hi">hereticos</td>
<td class="following">. Et vidit ibi cum eis dictum
Petrum de Sancto Andrea et
...</td>
</tr>
<tr>
<td class="previous">...r dicti Petri. Et ipse
testis adoravit ibi dictos </td>
<td class="hi">hereticos</td>
<td class="following">, sed non vidit alios adorare. Et
sunt VI anni vel circa...</td>
</tr>
What I'd like to do is wrap the text in <td class="hi"/> in a url that points to the source document viewable on the site. The site logic is quite 'clean', such that the first entry's <td class="hi"> would look like this:
<td class="hi">hereticos</td>
Where the url is a concat of
http://localhost:8081/exist/apps/deheresi/doc/
and the value of the respective result's ancestor node
tei:text/#xml:id
(which will always be an ancestor node of whatever tei:seg content is returned in the query).
I note there is a #link attribute available on the <config> parameter in kwic:summarize(), but I don't know how to dynamically get the source document nodes from the returned result in order to fill that in.
Many thanks in advance.
It turns out that the node $hit allows access to the rest of the source document (or a copy of it in memory). Thus, I was able to build a URL as string using $hit/ancestor
let $doclink := concat("http://localhost:8081/exist/apps/deheresi/doc/", $hit/ancestor::tei:text/data(#xml:id))
Then fed that string into the function parameter #link:
kwic:summarize($hit, <config width="80" table="yes" link="{$doclink}"/>)
Created a Data Frame ofp.1 which contains 13 rows and 2 columns using normal R file. The Data Frame is present in memory .
Now I want to use this table in xtable to print in R Markdown file.
`---
title: "abc"
output: html_document
---
<table border = 1 align='center'>
<tr>
<td style='text-align:center'>
```{r include=FALSE}
library(xtable)
```
```{r results='asis', echo=FALSE}
print(xtable(ofp.1),type='html',include.rownames=F)
```
</td>
</tr>
</table>
I am getting error Error in xtable(ofp.1) : object 'ofp.1' not found
Calls: <Anonymous> ... withCallingHandlers -> withVisible -> eval -> eval -> print -> xtable
Execution halted
what i Can figure out is that Markdown cannot find my Table though it is present in memory. Need Help .Thanks
proc ok { } {
exec echo "LIST OF TOKENS REQUESTED TO BE KILLED BY THE USER" >killed_file; #the information will be dumped in your a file named killed_file
global s0 s1 s2 s3 s4 s5
if {$s0} {exec echo "$s0 choice a" >>killed_file}
if {$s1} {exec echo "$s1 choice b" >>killed_file}
if {$s2} {exec echo "$s2 choice c" >>killed_file}
if {$s3} {exec echo "$s3 choice d" >>killed_file}
if {$s4} {exec echo "$s4 choice e" >>killed_file}
if {$s5} {exec echo "$s5 choice f" >>killed_file}
destroy .top
}
executing it says can't read s0 no such variable and same for other variables.
Presumably the variables s0 to s5 have not been set.
If you want to skip them when they do not exists you can use the command info exists name to test this. Also you are using a very strange method to write to your file. A more straightforward alternative (not tested) would be:
proc ok { } {
#the information will be dumped in your a file named killed_file
set kf [open killed_file w]
puts $kf "LIST OF TOKENS REQUESTED TO BE KILLED BY THE USER"
global s0 s1 s2 s3 s4 s5
if {[info exists s0]} {puts $kf "$s0 choice a"}
if {[info exists s1]} {puts $kf "$s1 choice b"}
if {[info exists s2]} {puts $kf "$s2 choice c"}
if {[info exists s3]} {puts $kf "$s3 choice d"}
if {[info exists s4]} {puts $kf "$s4 choice e"}
if {[info exists s5]} {puts $kf "$s5 choice f"}
close $kf
destroy .top
}
You probably should pass these in as arguments, shouldn't use 'exec' to write to a file, etc...
Below is my approach, which is bad and I'd not code in a real project, but much better than 'globals' and system calls to write to files:
proc ok {s0 s1 s2 s3 s4 s5} {
set FH [open killed_file 'w']
puts $FH "LIST OF TOKENS REQUESTED TO BE KILLED BY THE USER"
if {$s0} {puts $FH "$s0 choice a"}
if {$s1} {puts $FH "$s1 choice b"}
if {$s2} {puts $FH "$s2 choice c"}
if {$s3} {puts $FH "$s3 choice d"}
if {$s4} {puts $FH "$s4 choice e"}
if {$s5} {puts $FH "$s5 choice f"}
close $FH
destroy .top
}
If you give more details on what you are trying to achieve, we may be able to give a bit more help.