Merge pull request #435 from emqtt/0.15
0.15 release - Support 500K+ subscribers to subscribe one topic
|
@ -18,3 +18,4 @@ log/
|
||||||
*.swp
|
*.swp
|
||||||
*.so
|
*.so
|
||||||
examples
|
examples
|
||||||
|
docs/build/*
|
||||||
|
|
|
@ -19,3 +19,6 @@
|
||||||
[submodule "plugins/emqttd_recon"]
|
[submodule "plugins/emqttd_recon"]
|
||||||
path = plugins/emqttd_recon
|
path = plugins/emqttd_recon
|
||||||
url = https://github.com/emqtt/emqttd_recon.git
|
url = https://github.com/emqtt/emqttd_recon.git
|
||||||
|
[submodule "plugins/emqttd_plugin_redis"]
|
||||||
|
path = plugins/emqttd_plugin_redis
|
||||||
|
url = https://github.com/emqtt/emqttd_plugin_redis.git
|
||||||
|
|
46
CHANGELOG.md
|
@ -2,6 +2,52 @@
|
||||||
emqttd ChangeLog
|
emqttd ChangeLog
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
0.15.0-beta(2016-01-28)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
#### Highlights
|
||||||
|
|
||||||
|
Optimization for Route ETS insertion (#427)
|
||||||
|
|
||||||
|
Add Mongodb, Redis Plugins
|
||||||
|
|
||||||
|
Priority Message Queue Support
|
||||||
|
|
||||||
|
ReadTheDocs
|
||||||
|
|
||||||
|
|
||||||
|
#### Enhancements
|
||||||
|
|
||||||
|
Join/Leave the Cluster
|
||||||
|
|
||||||
|
Username Authentication: Default Users
|
||||||
|
|
||||||
|
Improve Cli commands: pubsub, bridges, trace
|
||||||
|
|
||||||
|
emqttd_mod_subscription: fix client_connected/3
|
||||||
|
|
||||||
|
|
||||||
|
#### BugFix
|
||||||
|
|
||||||
|
Fix dequeue/1 of emqttd_bridge...
|
||||||
|
|
||||||
|
Add emqttd:seed_now/0 function
|
||||||
|
|
||||||
|
|
||||||
|
#### Plugins
|
||||||
|
|
||||||
|
emqttd_plubin_mysql: changed mysql driver to mysql-otp
|
||||||
|
|
||||||
|
emqttd_plugin_pgsql: integrate with ecpool
|
||||||
|
|
||||||
|
emqttd_plugin_redis: first release
|
||||||
|
|
||||||
|
emqttd_plugin_mongo: first release
|
||||||
|
|
||||||
|
|
||||||
|
#### Benchmark
|
||||||
|
|
||||||
|
|
||||||
0.14.1-beta(2015-12-28)
|
0.14.1-beta(2015-12-28)
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -5,11 +5,9 @@ emqttd is a massively scalable and clusterable MQTT V3.1/V3.1.1 broker written i
|
||||||
|
|
||||||
emqttd requires Erlang R17+ to build.
|
emqttd requires Erlang R17+ to build.
|
||||||
|
|
||||||
**DON'T compile the broker with Erlang/OTP R18.0 which introduced a [binary memory leak](http://erlang.org/pipermail/erlang-questions/2015-September/086098.html).**
|
|
||||||
|
|
||||||
Demo Server: tcp://t.emqtt.io:1883
|
Demo Server: tcp://t.emqtt.io:1883
|
||||||
|
|
||||||
Twitter: [@emqtt](https://twitter.com/emqtt)
|
Follow us on Twitter: [@emqtt](https://twitter.com/emqtt)
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 26 KiB |
|
@ -1,55 +0,0 @@
|
||||||
## Transient Client/Session Sequence1
|
|
||||||
|
|
||||||
```
|
|
||||||
Client1->SM: {start_session, {true, ClientId, self()}}
|
|
||||||
SM-->Session: {destory, ClientId}
|
|
||||||
Session-->Session: {shutdown, destroy}
|
|
||||||
Session-->Client2: exit({shutdown, destroy})
|
|
||||||
Client2-->CM: {'DOWN', MRef, process, Pid, Reason}
|
|
||||||
SM-->Client1: {ok, SessPid}
|
|
||||||
Client1-->CM: {register, Client1}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Transient Client/Session Sequence2
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
Client1->SM: {start_session, {true, ClientId, self()}}
|
|
||||||
SM-->Session: {destory, ClientId}
|
|
||||||
Session-->Session: {shutdown, destroy}
|
|
||||||
SM-->Client1: {ok, SessPid}
|
|
||||||
Client1-->CM: {register, Client1}
|
|
||||||
Session-->Client2: exit({shutdown, destroy})
|
|
||||||
Client2-->CM: {'DOWN', MRef, process, Pid, Reason}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Persistent Client/Session Sequence1
|
|
||||||
|
|
||||||
```
|
|
||||||
Client1->SM: {start_session, {true, ClientId, self()}}
|
|
||||||
SM-->Session: {resume, ClientId, ClientPid}
|
|
||||||
Session-->Client2: {shutdown, conflict, {ClientId, Pid}}
|
|
||||||
Client2-->CM: {unregister, ClientId, self()}
|
|
||||||
SM-->Client1: {ok, SessPid}
|
|
||||||
Client1-->CM: {register, Client1}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Persistent Client/Session Sequence2
|
|
||||||
|
|
||||||
```
|
|
||||||
Client1->SM: {start_session, {true, ClientId, self()}}
|
|
||||||
SM-->Session: {resume, ClientId, ClientPid}
|
|
||||||
SM-->Client1: {ok, SessPid}
|
|
||||||
Client1-->CM: {register, Client1}
|
|
||||||
Session-->Client2: {shutdown, conflict, {ClientId, Pid}}
|
|
||||||
Client2-->CM: {unregister, ClientId, self()}
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
|
@ -1,847 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
|
||||||
<!--Created by yEd 3.13-->
|
|
||||||
<key for="graphml" id="d0" yfiles.type="resources"/>
|
|
||||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
|
||||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
|
||||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
|
||||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
|
||||||
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
|
||||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
|
||||||
<graph edgedefault="directed" id="G">
|
|
||||||
<data key="d7"/>
|
|
||||||
<node id="n0" yfiles.foldertype="group">
|
|
||||||
<data key="d4"/>
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ProxyAutoBoundsNode>
|
|
||||||
<y:Realizers active="0">
|
|
||||||
<y:GroupNode>
|
|
||||||
<y:Geometry height="334.0" width="357.5" x="211.25" y="155.0"/>
|
|
||||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#F5F5F5" type="dashed" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="node_width" borderDistance="0.0" configuration="CroppingLabel" fontFamily="Dialog" fontSize="18" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="27.19921875" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="357.5" x="0.0" y="153.400390625">emqttd cluster</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
|
||||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
|
||||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
|
||||||
</y:GroupNode>
|
|
||||||
<y:GroupNode>
|
|
||||||
<y:Geometry height="50.0" width="50.0" x="180.0" y="140.0"/>
|
|
||||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
|
||||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
|
||||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
|
||||||
</y:GroupNode>
|
|
||||||
</y:Realizers>
|
|
||||||
</y:ProxyAutoBoundsNode>
|
|
||||||
</data>
|
|
||||||
<graph edgedefault="directed" id="n0:">
|
|
||||||
<node id="n0::n0">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="226.25" y="170.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n1">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="309.25" y="223.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n2">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="268.25" y="223.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n3">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="249.25" y="254.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n4">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="284.25" y="254.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n5">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="284.25" y="193.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n6">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="428.75" y="170.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n7">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="511.75" y="223.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n8">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="470.75" y="223.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n9">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="451.75" y="254.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n10">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="486.75" y="254.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n11">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="486.75" y="193.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n12">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="226.25" y="350.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n13">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="309.25" y="403.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n14">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="268.25" y="403.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n15">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="249.25" y="434.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n16">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="284.25" y="434.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n17">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="284.25" y="373.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n18">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="428.75" y="350.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n19">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="511.75" y="403.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n20">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="470.75" y="403.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n21">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="451.75" y="434.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n22">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="486.75" y="434.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n23">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="486.75" y="373.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
</graph>
|
|
||||||
</node>
|
|
||||||
<node id="n1">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="81.75" y="209.5"/>
|
|
||||||
<y:Fill color="#3182BD" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n2">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="81.75" y="389.5"/>
|
|
||||||
<y:Fill color="#756BB1" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n3">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="654.25" y="209.5"/>
|
|
||||||
<y:Fill color="#756BB1" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n4">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="654.25" y="389.5"/>
|
|
||||||
<y:Fill color="#3182BD" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<edge id="n0::e0" source="n0::n0" target="n0::n6">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e1" source="n0::n0" target="n0::n12">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e2" source="n0::n0" target="n0::n18">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e3" source="n0::n2" target="n0::n5">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e4" source="n0::n3" target="n0::n2">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e5" source="n0::n4" target="n0::n2">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e6" source="n0::n5" target="n0::n1">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e7" source="n0::n6" target="n0::n18">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e8" source="n0::n8" target="n0::n11">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e9" source="n0::n9" target="n0::n8">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e10" source="n0::n10" target="n0::n8">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e11" source="n0::n11" target="n0::n7">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e12" source="n0::n12" target="n0::n18">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e13" source="n0::n12" target="n0::n6">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e14" source="n0::n14" target="n0::n17">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e15" source="n0::n15" target="n0::n14">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e16" source="n0::n16" target="n0::n14">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e17" source="n0::n17" target="n0::n13">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e18" source="n0::n20" target="n0::n23">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e19" source="n0::n21" target="n0::n20">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e20" source="n0::n22" target="n0::n20">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e21" source="n0::n23" target="n0::n19">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e0" source="n0::n18" target="n4">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e1" source="n1" target="n0::n0">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e2" source="n2" target="n0::n12">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e3" source="n0::n6" target="n3">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
</graph>
|
|
||||||
<data key="d0">
|
|
||||||
<y:Resources/>
|
|
||||||
</data>
|
|
||||||
</graphml>
|
|
Before Width: | Height: | Size: 26 KiB |
|
@ -1,274 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
|
||||||
<!--Created by yEd 3.13-->
|
|
||||||
<key for="graphml" id="d0" yfiles.type="resources"/>
|
|
||||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
|
||||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
|
||||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
|
||||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
|
||||||
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
|
||||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
|
||||||
<graph edgedefault="directed" id="G">
|
|
||||||
<data key="d7"/>
|
|
||||||
<node id="n0">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="130.0" y="200.0"/>
|
|
||||||
<y:Fill color="#3182BD" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n1">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="130.0" y="279.0"/>
|
|
||||||
<y:Fill color="#756BB1" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.6328125" x="16.68359375" y="13.43359375">P<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n2">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="458.0" y="202.5"/>
|
|
||||||
<y:Fill color="#756BB1" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n3">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="458.0" y="277.5"/>
|
|
||||||
<y:Fill color="#3182BD" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.462890625" x="16.7685546875" y="13.43359375">S<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n4">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="250.0" y="193.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n5">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="333.0" y="246.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n6">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="292.0" y="246.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n7">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="273.0" y="277.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n8">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="308.0" y="277.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n9">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="308.0" y="216.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<edge id="e0" source="n0" target="n3">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
|
||||||
<y:Point x="270.0" y="222.5"/>
|
|
||||||
<y:Point x="360.0" y="300.0"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="plain"/>
|
|
||||||
<y:BendStyle smoothed="true"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e1" source="n1" target="n2">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
|
||||||
<y:Point x="270.0" y="300.0"/>
|
|
||||||
<y:Point x="360.0" y="225.0"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="plain"/>
|
|
||||||
<y:BendStyle smoothed="true"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e2" source="n6" target="n9">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e3" source="n7" target="n6">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e4" source="n8" target="n6">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e5" source="n9" target="n5">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
</graph>
|
|
||||||
<data key="d0">
|
|
||||||
<y:Resources/>
|
|
||||||
</data>
|
|
||||||
</graphml>
|
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 26 KiB |
|
@ -1,234 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
|
||||||
<!--Created by yEd 3.14.2-->
|
|
||||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
|
||||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
|
||||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
|
||||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
|
||||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
|
||||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
|
||||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
|
||||||
<graph edgedefault="directed" id="G">
|
|
||||||
<data key="d0"/>
|
|
||||||
<node id="n0">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="60.72000000000003" width="64.55999999999995" x="-371.08000000000004" y="-960.3600000000001"/>
|
|
||||||
<y:Fill color="#FFCC00" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="26.486054687499973" y="21.293593750000014">T<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="diamond"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n1">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="30.0" width="30.0" x="-585.0" y="-945.0"/>
|
|
||||||
<y:Fill color="#CCFFCC" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="19.890625" x="5.0546875" y="5.93359375">C1<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n2">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="30.0" width="30.0" x="-478.04" y="-945.0"/>
|
|
||||||
<y:Fill color="#99CCFF" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="18.05078125" x="5.974609375" y="5.93359375">S1<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n3">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="30.0" width="30.0" x="-122.60000000000014" y="-945.0"/>
|
|
||||||
<y:Fill color="#CCFFCC" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="19.890625" x="5.0546875" y="5.93359375">C2<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n4">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="30.0" width="30.0" x="-229.56000000000012" y="-945.0"/>
|
|
||||||
<y:Fill color="#99CCFF" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="18.05078125" x="5.974609375" y="5.93359375">S2<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n5">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="18.715277777777715" width="82.08333333333326" x="-255.60166666666674" y="-879.3576388888889"/>
|
|
||||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="41.587890625" x="20.24772135416663" y="0.29123263888891415">Queue<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n6">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="18.715277777777715" width="82.08333333333326" x="-504.08166666666665" y="-879.3576388888889"/>
|
|
||||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="41.587890625" x="20.24772135416663" y="0.29123263888891415">Queue<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="rectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<edge id="e0" source="n0" target="n4">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="free" modelPosition="anywhere" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="54.8359375" x="6.2584301757812" y="-23.026406250000264">Dispatch<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e1" source="n4" target="n3">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="free" modelPosition="anywhere" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="44.41796875" x="13.971013183593755" y="-23.66640625000025">Deliver<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e2" source="n1" target="n2">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="free" modelPosition="anywhere" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="94.556640625" x="43.54167968749999" y="-36.46640625000032">Publish QoS1/2<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e3" source="n2" target="n0">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e4" source="n1" target="n0">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="-32.24675781249998" ty="0.0">
|
|
||||||
<y:Point x="-464.4087997380702" y="-990.0"/>
|
|
||||||
</y:Path>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="free" modelPosition="anywhere" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="80.330078125" x="53.093395996093705" y="-79.45582275390632">Publish Qos0<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e5" source="n4" target="n5">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e6" source="n2" target="n6">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
</graph>
|
|
||||||
<data key="d7">
|
|
||||||
<y:Resources/>
|
|
||||||
</data>
|
|
||||||
</graphml>
|
|
|
@ -1,14 +0,0 @@
|
||||||
## QoS0 Publish Sequence
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
title QoS0 Publish Sequence
|
|
||||||
|
|
||||||
C1->PubSub: Publish QoS0
|
|
||||||
PubSub-->S2: Dispatch QoS0
|
|
||||||
S2-->C2: Deliver QoS0
|
|
||||||
```
|
|
||||||
|
|
||||||
## QoS1 Publish Sequence
|
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,993 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
|
||||||
<!--Created by yEd 3.13-->
|
|
||||||
<key for="graphml" id="d0" yfiles.type="resources"/>
|
|
||||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
|
||||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
|
||||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
|
||||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
|
||||||
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
|
|
||||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
|
||||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
|
||||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
|
||||||
<graph edgedefault="directed" id="G">
|
|
||||||
<data key="d7"/>
|
|
||||||
<node id="n0" yfiles.foldertype="group">
|
|
||||||
<data key="d4"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ProxyAutoBoundsNode>
|
|
||||||
<y:Realizers active="0">
|
|
||||||
<y:GroupNode>
|
|
||||||
<y:Geometry height="334.0" width="357.5" x="242.0" y="185.0"/>
|
|
||||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#F5F5F5" type="dashed" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="node_width" borderDistance="0.0" configuration="CroppingLabel" fontFamily="Dialog" fontSize="18" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="27.19921875" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="357.5" x="0.0" y="153.400390625">emqttd broker cluster</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
|
||||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
|
||||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
|
||||||
</y:GroupNode>
|
|
||||||
<y:GroupNode>
|
|
||||||
<y:Geometry height="50.0" width="50.0" x="180.0" y="140.0"/>
|
|
||||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
|
||||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
|
||||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
|
||||||
</y:GroupNode>
|
|
||||||
</y:Realizers>
|
|
||||||
</y:ProxyAutoBoundsNode>
|
|
||||||
</data>
|
|
||||||
<graph edgedefault="directed" id="n0:">
|
|
||||||
<node id="n0::n0">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="257.0" y="200.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n1">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="340.0" y="253.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n2">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="299.0" y="253.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n3">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="280.0" y="284.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n4">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="315.0" y="284.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n5">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="315.0" y="223.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n6">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="459.5" y="200.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n7">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="542.5" y="253.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n8">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="501.5" y="253.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n9">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="482.5" y="284.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n10">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="517.5" y="284.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n11">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="517.5" y="223.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n12">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="257.0" y="380.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n13">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="340.0" y="433.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n14">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="299.0" y="433.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n15">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="280.0" y="464.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n16">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="315.0" y="464.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n17">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="315.0" y="403.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n18">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="124.0" width="125.0" x="459.5" y="380.0"/>
|
|
||||||
<y:Fill color="#74C476" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#74C476" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="60.5" y="60.0">
|
|
||||||
<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n19">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="542.5" y="433.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.587890625" x="2.2060546875" y="-0.56640625">#<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n20">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="501.5" y="433.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="13.5390625" x="1.23046875" y="-0.56640625">+<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n21">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="482.5" y="464.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="11.359375" x="2.3203125" y="-0.56640625">x<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n22">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="517.5" y="464.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="10.26953125" x="2.865234375" y="-0.56640625">y<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n0::n23">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="17.0" width="16.0" x="517.5" y="403.5"/>
|
|
||||||
<y:Fill color="#FD8D3C" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="8.48828125" x="3.755859375" y="-0.56640625">t<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
</graph>
|
|
||||||
</node>
|
|
||||||
<node id="n1">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="257.0" y="596.5"/>
|
|
||||||
<y:Fill color="#3182BD" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="37.861328125" x="3.0693359375" y="13.43359375">Client<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n2">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="338.0" y="567.5"/>
|
|
||||||
<y:Fill color="#756BB1" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.994140625" x="0.5029296875" y="13.43359375">Sensor<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n3">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="447.5" y="589.5"/>
|
|
||||||
<y:Fill color="#756BB1" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#756BB1" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.994140625" x="0.5029296875" y="13.43359375">Sensor<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n4">
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="44.0" x="542.5" y="596.5"/>
|
|
||||||
<y:Fill color="#3182BD" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#3182BD" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="37.861328125" x="3.0693359375" y="13.43359375">Client<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="ellipse"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n5">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="64.0" width="357.5" x="242.0" y="58.0"/>
|
|
||||||
<y:Fill color="#BDBDBD" transparent="false"/>
|
|
||||||
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="109.533203125" x="123.9833984375" y="22.933593750000014">Application Server<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n6">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="85.0" x="71.5" y="239.5"/>
|
|
||||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.50390625" x="28.248046875" y="13.43359375">Web<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n7">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="45.0" width="85.0" x="71.5" y="419.5"/>
|
|
||||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.50390625" x="28.248046875" y="13.43359375">Web<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n8">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="64.0" width="49.0" x="685.0" y="230.0"/>
|
|
||||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="43.052734375" x="2.9736328125" y="22.93359375">iPhone<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<node id="n9">
|
|
||||||
<data key="d5"/>
|
|
||||||
<data key="d6">
|
|
||||||
<y:ShapeNode>
|
|
||||||
<y:Geometry height="64.0" width="49.0" x="685.0" y="410.0"/>
|
|
||||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
|
||||||
<y:BorderStyle color="#C0C0C0" type="line" width="1.0"/>
|
|
||||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="50.58203125" x="-0.791015625" y="22.93359375">Android<y:LabelModel>
|
|
||||||
<y:SmartNodeLabelModel distance="4.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
</y:NodeLabel>
|
|
||||||
<y:Shape type="roundrectangle"/>
|
|
||||||
</y:ShapeNode>
|
|
||||||
</data>
|
|
||||||
</node>
|
|
||||||
<edge id="n0::e0" source="n0::n0" target="n0::n6">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e1" source="n0::n0" target="n0::n12">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e2" source="n0::n2" target="n0::n5">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e3" source="n0::n3" target="n0::n2">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e4" source="n0::n4" target="n0::n2">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e5" source="n0::n5" target="n0::n1">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e6" source="n0::n6" target="n0::n18">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e7" source="n0::n8" target="n0::n11">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e8" source="n0::n9" target="n0::n8">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e9" source="n0::n10" target="n0::n8">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e10" source="n0::n11" target="n0::n7">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e11" source="n0::n12" target="n0::n18">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#74C476" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e12" source="n0::n14" target="n0::n17">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e13" source="n0::n15" target="n0::n14">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e14" source="n0::n16" target="n0::n14">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e15" source="n0::n17" target="n0::n13">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e16" source="n0::n20" target="n0::n23">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e17" source="n0::n21" target="n0::n20">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e18" source="n0::n22" target="n0::n20">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="n0::e19" source="n0::n23" target="n0::n19">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#FD8D3C" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="none"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e0" source="n0::n18" target="n4">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e1" source="n2" target="n0::n12">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e2" source="n0::n18" target="n3">
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="-22.749321892003195" sy="57.76129269460557" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#756BB1" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="-31.872099564693826" y="37.92230224609375">MQTT<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e3" source="n5" target="n0::n0">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="-101.25" sy="32.0078125" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#BDBDBD" type="line" width="1.0"/>
|
|
||||||
<y:Arrows source="none" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-17.3134765625" y="29.92578125">HTTP<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e4" source="n5" target="n0::n6">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="101.25" sy="31.9765625" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#BDBDBD" type="dashed" width="2.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="-19.4169921875" y="29.92284393310547">MQTT<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e5" source="n6" target="n0::n0">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="66.671875" x="16.89422607421875" y="-9.06640625">WebSocket<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e6" source="n7" target="n0::n12">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="66.671875" x="16.89422607421875" y="-9.06640625">WebSocket<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e7" source="n0::n6" target="n8">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="30.8330078125" y="-9.06640625">MQTT<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e8" source="n0::n18" target="n9">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#C0C0C0" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="30.8330078125" y="-9.06640625">MQTT<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
<edge id="e9" source="n1" target="n0::n12">
|
|
||||||
<data key="d9"/>
|
|
||||||
<data key="d10">
|
|
||||||
<y:PolyLineEdge>
|
|
||||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
|
||||||
<y:LineStyle color="#3182BD" type="dashed" width="1.0"/>
|
|
||||||
<y:Arrows source="standard" target="standard"/>
|
|
||||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="38.833984375" x="-8.698938402078966" y="-55.90826416015625">MQTT<y:LabelModel>
|
|
||||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
|
||||||
</y:LabelModel>
|
|
||||||
<y:ModelParameter>
|
|
||||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
|
|
||||||
</y:ModelParameter>
|
|
||||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
|
||||||
</y:EdgeLabel>
|
|
||||||
<y:BendStyle smoothed="false"/>
|
|
||||||
</y:PolyLineEdge>
|
|
||||||
</data>
|
|
||||||
</edge>
|
|
||||||
</graph>
|
|
||||||
<data key="d0">
|
|
||||||
<y:Resources/>
|
|
||||||
</data>
|
|
||||||
</graphml>
|
|
BIN
doc/emqttd.png
Before Width: | Height: | Size: 40 KiB |
|
@ -1,7 +0,0 @@
|
||||||
sup(one_for_all)
|
|
||||||
manager
|
|
||||||
pool_sup(one_for_one)
|
|
||||||
worker1
|
|
||||||
worker2
|
|
||||||
...
|
|
||||||
workerN
|
|
BIN
doc/rfc6455.pdf
19
doc/uuid.md
|
@ -1,19 +0,0 @@
|
||||||
## Mongodb ObjectId
|
|
||||||
|
|
||||||
* 4-byte value representing the seconds since the Unix epoch,
|
|
||||||
* 3-byte machine identifier,
|
|
||||||
* 2-byte process id, and
|
|
||||||
* 3-byte counter, starting with a random value.
|
|
||||||
|
|
||||||
## Flake Id
|
|
||||||
|
|
||||||
* 64bits Timestamp
|
|
||||||
* 48bits WorkerId
|
|
||||||
* 16bits Sequence
|
|
||||||
|
|
||||||
## emqttd Id
|
|
||||||
|
|
||||||
* 64bits Timestamp: erlang:now(), erlang:system_time
|
|
||||||
* 48bits (node+pid): Node + Pid -> Integer
|
|
||||||
* 16bits Sequence: PktId
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Banner
|
%% Banner
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-define(COPYRIGHT, "Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>").
|
-define(COPYRIGHT, "Copyright (C) 2012-2016, Feng Lee <feng@emqtt.io>").
|
||||||
|
|
||||||
-define(LICENSE_MESSAGE, "Licensed under MIT").
|
-define(LICENSE_MESSAGE, "Licensed under MIT").
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @Copyright (C) 2012-2015, Feng Lee <feng@emqtt.io>
|
%%% @Copyright (C) 2012-2016, Feng Lee <feng@emqtt.io>
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit c6a532d49d2b479551bfd3b8d278d40c99e96ae3
|
|
@ -85,9 +85,6 @@
|
||||||
{client, [
|
{client, [
|
||||||
%% Socket is connected, but no 'CONNECT' packet received
|
%% Socket is connected, but no 'CONNECT' packet received
|
||||||
{idle_timeout, 10} %% seconds
|
{idle_timeout, 10} %% seconds
|
||||||
%TODO: Network ingoing limit
|
|
||||||
%{ingoing_rate_limit, '64KB/s'}
|
|
||||||
%TODO: Reconnet control
|
|
||||||
]},
|
]},
|
||||||
%% Session
|
%% Session
|
||||||
{session, [
|
{session, [
|
||||||
|
@ -111,11 +108,17 @@
|
||||||
{expired_after, 48}
|
{expired_after, 48}
|
||||||
|
|
||||||
]},
|
]},
|
||||||
%% Session
|
%% Queue
|
||||||
{queue, [
|
{queue, [
|
||||||
%% Max queue length. enqueued messages when persistent client disconnected,
|
%% simple | priority
|
||||||
|
{type, simple},
|
||||||
|
|
||||||
|
%% Topic Priority: 0~255, Default is 0
|
||||||
|
%% {priority, [{"topic/1", 10}, {"topic/2", 8}]},
|
||||||
|
|
||||||
|
%% Max queue length. Enqueued messages when persistent client disconnected,
|
||||||
%% or inflight window is full.
|
%% or inflight window is full.
|
||||||
{max_length, 100},
|
{max_length, infinity},
|
||||||
|
|
||||||
%% Low-water mark of queued messages
|
%% Low-water mark of queued messages
|
||||||
{low_watermark, 0.2},
|
{low_watermark, 0.2},
|
||||||
|
@ -147,7 +150,7 @@
|
||||||
%% PubSub and Router
|
%% PubSub and Router
|
||||||
{pubsub, [
|
{pubsub, [
|
||||||
%% Default should be scheduler numbers
|
%% Default should be scheduler numbers
|
||||||
%% {pool_size, 8},
|
{pool_size, 8},
|
||||||
|
|
||||||
%% Subscription: disc | ram | false
|
%% Subscription: disc | ram | false
|
||||||
{subscription, ram},
|
{subscription, ram},
|
||||||
|
|
|
@ -103,11 +103,17 @@
|
||||||
{expired_after, 48}
|
{expired_after, 48}
|
||||||
|
|
||||||
]},
|
]},
|
||||||
%% Session
|
%% Queue
|
||||||
{queue, [
|
{queue, [
|
||||||
%% Max queue length. enqueued messages when persistent client disconnected,
|
%% simple | priority
|
||||||
|
{type, simple},
|
||||||
|
|
||||||
|
%% Topic Priority: 0~255, Default is 0
|
||||||
|
%% {priority, [{"topic/1", 10}, {"topic/2", 8}]},
|
||||||
|
|
||||||
|
%% Max queue length. Enqueued messages when persistent client disconnected,
|
||||||
%% or inflight window is full.
|
%% or inflight window is full.
|
||||||
{max_length, 100},
|
{max_length, infinity},
|
||||||
|
|
||||||
%% Low-water mark of queued messages
|
%% Low-water mark of queued messages
|
||||||
{low_watermark, 0.2},
|
{low_watermark, 0.2},
|
||||||
|
@ -139,7 +145,7 @@
|
||||||
%% PubSub and Router
|
%% PubSub and Router
|
||||||
{pubsub, [
|
{pubsub, [
|
||||||
%% Default should be scheduler numbers
|
%% Default should be scheduler numbers
|
||||||
%% {pool_size, 8},
|
{pool_size, 8},
|
||||||
|
|
||||||
%% Subscription: disc | ram | false
|
%% Subscription: disc | ram | false
|
||||||
{subscription, ram},
|
{subscription, ram},
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
-env ERTS_MAX_PORTS 8192
|
-env ERTS_MAX_PORTS 8192
|
||||||
|
|
||||||
|
## Mnesia and SSL will create temporary ets tables.
|
||||||
-env ERL_MAX_ETS_TABLES 1024
|
-env ERL_MAX_ETS_TABLES 1024
|
||||||
|
|
||||||
## Tweak GC to run more often
|
## Tweak GC to run more often
|
||||||
|
|
|
@ -13,4 +13,3 @@
|
||||||
{mod, {emqttd_app, []}},
|
{mod, {emqttd_app, []}},
|
||||||
{env, []}
|
{env, []}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -23,12 +23,16 @@
|
||||||
%%%
|
%%%
|
||||||
%%% @author Feng Lee <feng@emqtt.io>
|
%%% @author Feng Lee <feng@emqtt.io>
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqttd).
|
-module(emqttd).
|
||||||
|
|
||||||
-export([start/0, env/1, env/2,
|
-export([start/0, env/1, env/2,
|
||||||
start_listeners/0, stop_listeners/0,
|
start_listeners/0, stop_listeners/0,
|
||||||
load_all_mods/0, is_mod_enabled/1,
|
load_all_mods/0, is_mod_enabled/1,
|
||||||
is_running/1]).
|
is_running/1, seed_now/0]).
|
||||||
|
|
||||||
|
%% Utility functions.
|
||||||
|
-export([reg_name/2]).
|
||||||
|
|
||||||
-define(MQTT_SOCKOPTS, [
|
-define(MQTT_SOCKOPTS, [
|
||||||
binary,
|
binary,
|
||||||
|
@ -122,10 +126,8 @@ load_mod({Name, Opts}) ->
|
||||||
is_mod_enabled(Name) ->
|
is_mod_enabled(Name) ->
|
||||||
env(modules, Name) =/= undefined.
|
env(modules, Name) =/= undefined.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Is running?
|
%% @doc Is running?
|
||||||
%% @end
|
-spec is_running(node()) -> boolean().
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
is_running(Node) ->
|
is_running(Node) ->
|
||||||
case rpc:call(Node, erlang, whereis, [?APP]) of
|
case rpc:call(Node, erlang, whereis, [?APP]) of
|
||||||
{badrpc, _} -> false;
|
{badrpc, _} -> false;
|
||||||
|
@ -133,3 +135,16 @@ is_running(Node) ->
|
||||||
Pid when is_pid(Pid) -> true
|
Pid when is_pid(Pid) -> true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec reg_name(module(), pos_integer()) -> atom().
|
||||||
|
reg_name(M, Id) when is_atom(M), is_integer(Id) ->
|
||||||
|
list_to_atom(lists:concat([M, "_", Id])).
|
||||||
|
|
||||||
|
seed_now() ->
|
||||||
|
case erlang:function_exported(erlang, timestamp, 0) of
|
||||||
|
true -> %% R18
|
||||||
|
random:seed(erlang:timestamp());
|
||||||
|
false ->
|
||||||
|
%% compress 'now()' warning...
|
||||||
|
random:seed(os:timestamp())
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -27,6 +27,10 @@
|
||||||
|
|
||||||
-include("emqttd.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
|
-export([passwd_hash/2]).
|
||||||
|
|
||||||
|
-type hash_type() :: plain | md5 | sha | sha256.
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% Auth behavihour
|
%%% Auth behavihour
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
@ -53,3 +57,21 @@ behaviour_info(_Other) ->
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
%% @doc Password Hash
|
||||||
|
-spec passwd_hash(hash_type(), binary()) -> binary().
|
||||||
|
passwd_hash(plain, Password) ->
|
||||||
|
Password;
|
||||||
|
passwd_hash(md5, Password) ->
|
||||||
|
hexstring(crypto:hash(md5, Password));
|
||||||
|
passwd_hash(sha, Password) ->
|
||||||
|
hexstring(crypto:hash(sha, Password));
|
||||||
|
passwd_hash(sha256, Password) ->
|
||||||
|
hexstring(crypto:hash(sha256, Password)).
|
||||||
|
|
||||||
|
hexstring(<<X:128/big-unsigned-integer>>) ->
|
||||||
|
iolist_to_binary(io_lib:format("~32.16.0b", [X]));
|
||||||
|
hexstring(<<X:160/big-unsigned-integer>>) ->
|
||||||
|
iolist_to_binary(io_lib:format("~40.16.0b", [X]));
|
||||||
|
hexstring(<<X:256/big-unsigned-integer>>) ->
|
||||||
|
iolist_to_binary(io_lib:format("~64.16.0b", [X])).
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -71,6 +71,9 @@ add_user(Username, Password) ->
|
||||||
User = #?AUTH_USERNAME_TAB{username = Username, password = hash(Password)},
|
User = #?AUTH_USERNAME_TAB{username = Username, password = hash(Password)},
|
||||||
mnesia:transaction(fun mnesia:write/1, [User]).
|
mnesia:transaction(fun mnesia:write/1, [User]).
|
||||||
|
|
||||||
|
add_default_user(Username, Password) ->
|
||||||
|
add_user(bin(Username), bin(Password)).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc Lookup user by username
|
%% @doc Lookup user by username
|
||||||
%% @end
|
%% @end
|
||||||
|
@ -98,13 +101,16 @@ all_users() ->
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% emqttd_auth callbacks
|
%%% emqttd_auth callbacks
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
init(Opts) ->
|
init(DefautUsers) ->
|
||||||
mnesia:create_table(?AUTH_USERNAME_TAB, [
|
mnesia:create_table(?AUTH_USERNAME_TAB, [
|
||||||
{disc_copies, [node()]},
|
{disc_copies, [node()]},
|
||||||
{attributes, record_info(fields, ?AUTH_USERNAME_TAB)}]),
|
{attributes, record_info(fields, ?AUTH_USERNAME_TAB)}]),
|
||||||
mnesia:add_table_copy(?AUTH_USERNAME_TAB, node(), disc_copies),
|
mnesia:add_table_copy(?AUTH_USERNAME_TAB, node(), disc_copies),
|
||||||
|
lists:foreach(fun({Username, Password}) ->
|
||||||
|
add_default_user(Username, Password)
|
||||||
|
end, DefautUsers),
|
||||||
emqttd_ctl:register_cmd(users, {?MODULE, cli}, []),
|
emqttd_ctl:register_cmd(users, {?MODULE, cli}, []),
|
||||||
{ok, Opts}.
|
{ok, []}.
|
||||||
|
|
||||||
check(#mqtt_client{username = undefined}, _Password, _Opts) ->
|
check(#mqtt_client{username = undefined}, _Password, _Opts) ->
|
||||||
{error, "Username undefined"};
|
{error, "Username undefined"};
|
||||||
|
@ -136,8 +142,11 @@ md5_hash(SaltBin, Password) ->
|
||||||
erlang:md5(<<SaltBin/binary, Password/binary>>).
|
erlang:md5(<<SaltBin/binary, Password/binary>>).
|
||||||
|
|
||||||
salt() ->
|
salt() ->
|
||||||
{A1,A2,A3} = now(),
|
emqttd:seed_now(),
|
||||||
random:seed(A1, A2, A3),
|
|
||||||
Salt = random:uniform(16#ffffffff),
|
Salt = random:uniform(16#ffffffff),
|
||||||
<<Salt:32>>.
|
<<Salt:32>>.
|
||||||
|
|
||||||
|
bin(A) when is_atom(A) -> bin(atom_to_list(A));
|
||||||
|
bin(L) when is_list(L) -> list_to_binary(L);
|
||||||
|
bin(B) when is_binary(B) -> B.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -172,7 +172,7 @@ dequeue(State = #state{mqueue = MQ}) ->
|
||||||
{empty, MQ1} ->
|
{empty, MQ1} ->
|
||||||
State#state{mqueue = MQ1};
|
State#state{mqueue = MQ1};
|
||||||
{{value, Msg}, MQ1} ->
|
{{value, Msg}, MQ1} ->
|
||||||
handle_info({dispatch, Msg}, State),
|
handle_info({dispatch, Msg#mqtt_message.topic, Msg}, State),
|
||||||
dequeue(State#state{mqueue = MQ1})
|
dequeue(State#state{mqueue = MQ1})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -218,7 +218,7 @@ stop_tick(TRef) ->
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
random:seed(os:timestamp()),
|
emqttd:seed_now(),
|
||||||
ets:new(?BROKER_TAB, [set, public, named_table]),
|
ets:new(?BROKER_TAB, [set, public, named_table]),
|
||||||
% Create $SYS Topics
|
% Create $SYS Topics
|
||||||
emqttd_pubsub:create(topic, <<"$SYS/brokers">>),
|
emqttd_pubsub:create(topic, <<"$SYS/brokers">>),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -101,7 +101,7 @@ broker(["metrics"]) ->
|
||||||
end, lists:sort(emqttd_metrics:all()));
|
end, lists:sort(emqttd_metrics:all()));
|
||||||
|
|
||||||
broker(["pubsub"]) ->
|
broker(["pubsub"]) ->
|
||||||
Pubsubs = supervisor:which_children(emqttd_pubsub_sup),
|
Pubsubs = supervisor:which_children(emqttd_pubsub_sup:pubsub_pool()),
|
||||||
foreach(fun({{_, Id}, Pid, _, _}) ->
|
foreach(fun({{_, Id}, Pid, _, _}) ->
|
||||||
ProcInfo = erlang:process_info(Pid, ?PROC_INFOKEYS),
|
ProcInfo = erlang:process_info(Pid, ?PROC_INFOKEYS),
|
||||||
?PRINT("pubsub: ~w~n", [Id]),
|
?PRINT("pubsub: ~w~n", [Id]),
|
||||||
|
@ -323,7 +323,7 @@ plugins(_) ->
|
||||||
|
|
||||||
bridges(["list"]) ->
|
bridges(["list"]) ->
|
||||||
foreach(fun({{Node, Topic}, _Pid}) ->
|
foreach(fun({{Node, Topic}, _Pid}) ->
|
||||||
?PRINT("bridge: ~s ~s~n", [Node, Topic])
|
?PRINT("bridge: ~s--~s-->~s~n", [node(), Topic, Node])
|
||||||
end, emqttd_bridge_sup:bridges());
|
end, emqttd_bridge_sup:bridges());
|
||||||
|
|
||||||
bridges(["options"]) ->
|
bridges(["options"]) ->
|
||||||
|
@ -449,7 +449,7 @@ trace(_) ->
|
||||||
{"trace topic <Topic> off", "stop to trace Topic"}]).
|
{"trace topic <Topic> off", "stop to trace Topic"}]).
|
||||||
|
|
||||||
trace_on(Who, Name, LogFile) ->
|
trace_on(Who, Name, LogFile) ->
|
||||||
case emqttd_trace:start_trace({Who, bin(Name)}, LogFile) of
|
case emqttd_trace:start_trace({Who, iolist_to_binary(Name)}, LogFile) of
|
||||||
ok ->
|
ok ->
|
||||||
?PRINT("trace ~s ~s successfully.~n", [Who, Name]);
|
?PRINT("trace ~s ~s successfully.~n", [Who, Name]);
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
|
@ -457,7 +457,7 @@ trace_on(Who, Name, LogFile) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
trace_off(Who, Name) ->
|
trace_off(Who, Name) ->
|
||||||
case emqttd_trace:stop_trace({Who, bin(Name)}) of
|
case emqttd_trace:stop_trace({Who, iolist_to_binary(Name)}) of
|
||||||
ok ->
|
ok ->
|
||||||
?PRINT("stop to trace ~s ~s successfully.~n", [Who, Name]);
|
?PRINT("stop to trace ~s ~s successfully.~n", [Who, Name]);
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
|
@ -507,17 +507,16 @@ print({{ClientId, _ClientPid}, SessInfo}) ->
|
||||||
awaiting_rel,
|
awaiting_rel,
|
||||||
awaiting_ack,
|
awaiting_ack,
|
||||||
awaiting_comp,
|
awaiting_comp,
|
||||||
created_at,
|
created_at],
|
||||||
subscriptions],
|
|
||||||
?PRINT("Session(~s, clean_sess=~s, max_inflight=~w, inflight_queue=~w, "
|
?PRINT("Session(~s, clean_sess=~s, max_inflight=~w, inflight_queue=~w, "
|
||||||
"message_queue=~w, message_dropped=~w, "
|
"message_queue=~w, message_dropped=~w, "
|
||||||
"awaiting_rel=~w, awaiting_ack=~w, awaiting_comp=~w, "
|
"awaiting_rel=~w, awaiting_ack=~w, awaiting_comp=~w, "
|
||||||
"created_at=~w, subscriptions=~s)~n",
|
"created_at=~w)~n",
|
||||||
[ClientId | [format(Key, proplists:get_value(Key, SessInfo)) || Key <- InfoKeys]]).
|
[ClientId | [format(Key, proplists:get_value(Key, SessInfo)) || Key <- InfoKeys]]).
|
||||||
|
|
||||||
print(topic, Topic, Records) ->
|
print(topic, Topic, Records) ->
|
||||||
Nodes = [Node || #mqtt_topic{node = Node} <- Records],
|
Nodes = [Node || #mqtt_topic{node = Node} <- Records],
|
||||||
?PRINT("~s: on ~p~n", [Topic, Nodes]);
|
?PRINT("~s: ~p~n", [Topic, Nodes]);
|
||||||
|
|
||||||
print(subscription, ClientId, Subscriptions) ->
|
print(subscription, ClientId, Subscriptions) ->
|
||||||
TopicTable = [{Topic, Qos} || #mqtt_subscription{topic = Topic, qos = Qos} <- Subscriptions],
|
TopicTable = [{Topic, Qos} || #mqtt_subscription{topic = Topic, qos = Qos} <- Subscriptions],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -283,7 +283,7 @@ key(counter, Metric) ->
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
random:seed(os:timestamp()),
|
emqttd:seed_now(),
|
||||||
Metrics = ?SYSTOP_BYTES ++ ?SYSTOP_PACKETS ++ ?SYSTOP_MESSAGES,
|
Metrics = ?SYSTOP_BYTES ++ ?SYSTOP_PACKETS ++ ?SYSTOP_MESSAGES,
|
||||||
% Create metrics table
|
% Create metrics table
|
||||||
ets:new(?METRIC_TAB, [set, public, named_table, {write_concurrency, true}]),
|
ets:new(?METRIC_TAB, [set, public, named_table, {write_concurrency, true}]),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
load(Opts) ->
|
load(Opts) ->
|
||||||
Topics = [{bin(Topic), QoS} || {Topic, QoS} <- Opts, ?IS_QOS(QoS)],
|
Topics = [{iolist_to_binary(Topic), QoS} || {Topic, QoS} <- Opts, ?IS_QOS(QoS)],
|
||||||
State = #state{topics = Topics, stored = lists:member(stored, Opts)},
|
State = #state{topics = Topics, stored = lists:member(stored, Opts)},
|
||||||
emqttd_broker:hook('client.connected', {?MODULE, client_connected},
|
emqttd_broker:hook('client.connected', {?MODULE, client_connected},
|
||||||
{?MODULE, client_connected, [State]}),
|
{?MODULE, client_connected, [State]}),
|
||||||
|
@ -52,7 +52,9 @@ client_connected(?CONNACK_ACCEPT, #mqtt_client{client_id = ClientId,
|
||||||
#state{topics = Topics, stored = Stored}) ->
|
#state{topics = Topics, stored = Stored}) ->
|
||||||
Replace = fun(Topic) -> rep(<<"$u">>, Username, rep(<<"$c">>, ClientId, Topic)) end,
|
Replace = fun(Topic) -> rep(<<"$u">>, Username, rep(<<"$c">>, ClientId, Topic)) end,
|
||||||
TopicTable = with_stored(Stored, ClientId, [{Replace(Topic), Qos} || {Topic, Qos} <- Topics]),
|
TopicTable = with_stored(Stored, ClientId, [{Replace(Topic), Qos} || {Topic, Qos} <- Topics]),
|
||||||
emqttd_client:subscribe(ClientPid, TopicTable).
|
emqttd_client:subscribe(ClientPid, TopicTable);
|
||||||
|
|
||||||
|
client_connected(_ConnAck, _Client, _State) -> ok.
|
||||||
|
|
||||||
with_stored(false, _ClientId, TopicTable) ->
|
with_stored(false, _ClientId, TopicTable) ->
|
||||||
TopicTable;
|
TopicTable;
|
||||||
|
@ -70,8 +72,3 @@ rep(<<"$u">>, undefined, Topic) ->
|
||||||
rep(<<"$u">>, Username, Topic) ->
|
rep(<<"$u">>, Username, Topic) ->
|
||||||
emqttd_topic:feed_var(<<"$u">>, Username, Topic).
|
emqttd_topic:feed_var(<<"$u">>, Username, Topic).
|
||||||
|
|
||||||
bin(B) when is_binary(B) ->
|
|
||||||
B;
|
|
||||||
bin(S) when is_list(S) ->
|
|
||||||
list_to_binary(S).
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -30,20 +30,21 @@
|
||||||
%%%
|
%%%
|
||||||
%%% If the broker restarted or crashed, all the messages queued will be gone.
|
%%% If the broker restarted or crashed, all the messages queued will be gone.
|
||||||
%%%
|
%%%
|
||||||
%%% Desgin of The Queue:
|
%%% Concept of Message Queue and Inflight Window:
|
||||||
|
%%%
|
||||||
%%% |<----------------- Max Len ----------------->|
|
%%% |<----------------- Max Len ----------------->|
|
||||||
%%% -----------------------------------------------
|
%%% -----------------------------------------------
|
||||||
%%% IN -> | Pending Messages | Inflight Window | -> Out
|
%%% IN -> | Messages Queue | Inflight Window | -> Out
|
||||||
%%% -----------------------------------------------
|
%%% -----------------------------------------------
|
||||||
%%% |<--- Win Size --->|
|
%%% |<--- Win Size --->|
|
||||||
%%%
|
%%%
|
||||||
%%%
|
%%%
|
||||||
%%% 1. Inflight Window to store the messages awaiting for ack.
|
%%% 1. Inflight Window to store the messages delivered and awaiting for puback.
|
||||||
%%%
|
%%%
|
||||||
%%% 2. Suspend IN messages when the queue is deactive, or inflight windows is full.
|
%%% 2. Enqueue messages when the inflight window is full.
|
||||||
%%%
|
%%%
|
||||||
%%% 3. If the queue is full, dropped qos0 messages if store_qos0 is true,
|
%%% 3. If the queue is full, dropped qos0 messages if store_qos0 is true,
|
||||||
%%% otherwise dropped the oldest pending one.
|
%%% otherwise dropped the oldest one.
|
||||||
%%%
|
%%%
|
||||||
%%% @end
|
%%% @end
|
||||||
%%%
|
%%%
|
||||||
|
@ -55,96 +56,161 @@
|
||||||
|
|
||||||
-include("emqttd_protocol.hrl").
|
-include("emqttd_protocol.hrl").
|
||||||
|
|
||||||
-export([new/3, name/1,
|
-export([new/3, type/1, name/1, is_empty/1, len/1, max_len/1, in/2, out/1, stats/1]).
|
||||||
is_empty/1, is_full/1,
|
|
||||||
len/1, max_len/1,
|
|
||||||
in/2, out/1,
|
|
||||||
stats/1]).
|
|
||||||
|
|
||||||
-define(LOW_WM, 0.2).
|
-define(LOW_WM, 0.2).
|
||||||
|
|
||||||
-define(HIGH_WM, 0.6).
|
-define(HIGH_WM, 0.6).
|
||||||
|
|
||||||
-record(mqueue, {name,
|
-type priority() :: {iolist(), pos_integer()}.
|
||||||
q = queue:new(), %% pending queue
|
|
||||||
len = 0, %% current queue len
|
|
||||||
low_wm = ?LOW_WM,
|
|
||||||
high_wm = ?HIGH_WM,
|
|
||||||
max_len = ?MAX_LEN,
|
|
||||||
qos0 = false,
|
|
||||||
dropped = 0,
|
|
||||||
alarm_fun}).
|
|
||||||
|
|
||||||
-type mqueue() :: #mqueue{}.
|
-type option() :: {type, simple | priority}
|
||||||
|
| {max_length, pos_integer() | infinity}
|
||||||
|
| {priority, list(priority())}
|
||||||
|
| {low_watermark, float()} %% Low watermark
|
||||||
|
| {high_watermark, float()} %% High watermark
|
||||||
|
| {queue_qos0, boolean()}. %% Queue Qos0?
|
||||||
|
|
||||||
-type mqueue_option() :: {max_length, pos_integer()} %% Max queue length
|
-type mqueue_option() :: {max_length, pos_integer()} %% Max queue length
|
||||||
| {low_watermark, float()} %% Low watermark
|
| {low_watermark, float()} %% Low watermark
|
||||||
| {high_watermark, float()} %% High watermark
|
| {high_watermark, float()} %% High watermark
|
||||||
| {queue_qos0, boolean()}. %% Queue Qos0
|
| {queue_qos0, boolean()}. %% Queue Qos0
|
||||||
|
|
||||||
-export_type([mqueue/0]).
|
-type stat() :: {max_len, infinity | pos_integer()}
|
||||||
|
| {len, non_neg_integer()}
|
||||||
|
| {dropped, non_neg_integer()}.
|
||||||
|
|
||||||
|
-record(mqueue, {type :: simple | priority,
|
||||||
|
name, q :: queue:queue() | priority_queue:q(),
|
||||||
|
%% priority table
|
||||||
|
pseq = 0, priorities = [],
|
||||||
|
%% len of simple queue
|
||||||
|
len = 0, max_len = ?MAX_LEN,
|
||||||
|
low_wm = ?LOW_WM, high_wm = ?HIGH_WM,
|
||||||
|
qos0 = false, dropped = 0,
|
||||||
|
alarm_fun}).
|
||||||
|
|
||||||
|
-type mqueue() :: #mqueue{}.
|
||||||
|
|
||||||
|
-export_type([mqueue/0, priority/0, option/0]).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc New Queue.
|
%% @doc New Queue.
|
||||||
%% @end
|
-spec new(iolist(), list(mqueue_option()), fun()) -> mqueue().
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec new(binary(), list(mqueue_option()), fun()) -> mqueue().
|
|
||||||
new(Name, Opts, AlarmFun) ->
|
new(Name, Opts, AlarmFun) ->
|
||||||
MaxLen = emqttd_opts:g(max_length, Opts, 1000),
|
Type = emqttd_opts:g(type, Opts, simple),
|
||||||
#mqueue{name = Name,
|
MaxLen = emqttd_opts:g(max_length, Opts, infinity),
|
||||||
max_len = MaxLen,
|
init_q(#mqueue{type = Type, name = iolist_to_binary(Name),
|
||||||
low_wm = round(MaxLen * emqttd_opts:g(low_watermark, Opts, ?LOW_WM)),
|
len = 0, max_len = MaxLen,
|
||||||
high_wm = round(MaxLen * emqttd_opts:g(high_watermark, Opts, ?HIGH_WM)),
|
low_wm = low_wm(MaxLen, Opts),
|
||||||
qos0 = emqttd_opts:g(queue_qos0, Opts, true),
|
high_wm = high_wm(MaxLen, Opts),
|
||||||
alarm_fun = AlarmFun}.
|
qos0 = emqttd_opts:g(queue_qos0, Opts, false),
|
||||||
|
alarm_fun = AlarmFun}, Opts).
|
||||||
|
|
||||||
|
init_q(MQ = #mqueue{type = simple}, _Opts) ->
|
||||||
|
MQ#mqueue{q = queue:new()};
|
||||||
|
init_q(MQ = #mqueue{type = priority}, Opts) ->
|
||||||
|
Priorities = emqttd_opts:g(priority, Opts, []),
|
||||||
|
init_p(Priorities, MQ#mqueue{q = priority_queue:new()}).
|
||||||
|
|
||||||
|
init_p([], MQ) ->
|
||||||
|
MQ;
|
||||||
|
init_p([{Topic, P} | L], MQ) ->
|
||||||
|
{_, MQ1} = insert_p(iolist_to_binary(Topic), P, MQ),
|
||||||
|
init_p(L, MQ1).
|
||||||
|
|
||||||
|
insert_p(Topic, P, MQ = #mqueue{priorities = Tab, pseq = Seq}) ->
|
||||||
|
<<PInt:48>> = <<P:8, (erlang:phash2(Topic)):32, Seq:8>>,
|
||||||
|
{PInt, MQ#mqueue{priorities = [{Topic, PInt} | Tab], pseq = Seq + 1}}.
|
||||||
|
|
||||||
|
low_wm(infinity, _Opts) ->
|
||||||
|
infinity;
|
||||||
|
low_wm(MaxLen, Opts) ->
|
||||||
|
round(MaxLen * emqttd_opts:g(low_watermark, Opts, ?LOW_WM)).
|
||||||
|
|
||||||
|
high_wm(infinity, _Opts) ->
|
||||||
|
infinity;
|
||||||
|
high_wm(MaxLen, Opts) ->
|
||||||
|
round(MaxLen * emqttd_opts:g(high_watermark, Opts, ?HIGH_WM)).
|
||||||
|
|
||||||
|
-spec name(mqueue()) -> iolist().
|
||||||
name(#mqueue{name = Name}) ->
|
name(#mqueue{name = Name}) ->
|
||||||
Name.
|
Name.
|
||||||
|
|
||||||
is_empty(#mqueue{len = 0}) -> true;
|
-spec type(mqueue()) -> atom().
|
||||||
is_empty(_MQ) -> false.
|
type(#mqueue{type = Type}) ->
|
||||||
|
Type.
|
||||||
|
|
||||||
is_full(#mqueue{len = Len, max_len = MaxLen})
|
is_empty(#mqueue{type = simple, len = Len}) -> Len =:= 0;
|
||||||
when Len =:= MaxLen -> true;
|
is_empty(#mqueue{type = priority, q = Q}) -> priority_queue:is_empty(Q).
|
||||||
is_full(_MQ) -> false.
|
|
||||||
|
|
||||||
len(#mqueue{len = Len}) -> Len.
|
len(#mqueue{type = simple, len = Len}) -> Len;
|
||||||
|
len(#mqueue{type = priority, q = Q}) -> priority_queue:len(Q).
|
||||||
|
|
||||||
max_len(#mqueue{max_len= MaxLen}) -> MaxLen.
|
max_len(#mqueue{max_len= MaxLen}) -> MaxLen.
|
||||||
|
|
||||||
stats(#mqueue{max_len = MaxLen, len = Len, dropped = Dropped}) ->
|
%% @doc Stats of the mqueue
|
||||||
[{max_len, MaxLen}, {len, Len}, {dropped, Dropped}].
|
-spec stats(mqueue()) -> [stat()].
|
||||||
|
stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped}) ->
|
||||||
%%------------------------------------------------------------------------------
|
[{len, case Type of
|
||||||
%% @doc Queue one message.
|
simple -> Len;
|
||||||
%% @end
|
priority -> priority_queue:len(Q)
|
||||||
%%------------------------------------------------------------------------------
|
end} | [{max_len, MaxLen}, {dropped, Dropped}]].
|
||||||
|
|
||||||
|
%% @doc Enqueue a message.
|
||||||
-spec in(mqtt_message(), mqueue()) -> mqueue().
|
-spec in(mqtt_message(), mqueue()) -> mqueue().
|
||||||
%% drop qos0
|
|
||||||
in(#mqtt_message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
|
in(#mqtt_message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
|
||||||
MQ;
|
MQ;
|
||||||
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = infinity}) ->
|
||||||
%% simply drop the oldest one if queue is full, improve later
|
MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};
|
||||||
in(Msg, MQ = #mqueue{q = Q, len = Len, max_len = MaxLen, dropped = Dropped})
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = MaxLen, dropped = Dropped})
|
||||||
when Len =:= MaxLen ->
|
when Len >= MaxLen ->
|
||||||
{{value, _OldMsg}, Q2} = queue:out(Q),
|
{{value, _Old}, Q2} = queue:out(Q),
|
||||||
%lager:error("MQueue(~s) drop ~s", [Name, emqttd_message:format(OldMsg)]),
|
|
||||||
MQ#mqueue{q = queue:in(Msg, Q2), dropped = Dropped +1};
|
MQ#mqueue{q = queue:in(Msg, Q2), dropped = Dropped +1};
|
||||||
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
||||||
|
maybe_set_alarm(MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1});
|
||||||
|
|
||||||
in(Msg, MQ = #mqueue{q = Q, len = Len}) ->
|
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
maybe_set_alarm(MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1}).
|
priorities = Priorities,
|
||||||
|
max_len = infinity}) ->
|
||||||
|
case lists:keysearch(Topic, 1, Priorities) of
|
||||||
|
{value, {_, Pri}} ->
|
||||||
|
MQ#mqueue{q = priority_queue:in(Msg, Pri, Q)};
|
||||||
|
false ->
|
||||||
|
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
||||||
|
MQ1#mqueue{q = priority_queue:in(Msg, Pri, Q)}
|
||||||
|
end;
|
||||||
|
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
|
priorities = Priorities,
|
||||||
|
max_len = MaxLen}) ->
|
||||||
|
case lists:keysearch(Topic, 1, Priorities) of
|
||||||
|
{value, {_, Pri}} ->
|
||||||
|
case priority_queue:plen(Pri, Q) >= MaxLen of
|
||||||
|
true ->
|
||||||
|
{_, Q1} = priority_queue:out(Pri, Q),
|
||||||
|
MQ#mqueue{q = priority_queue:in(Msg, Pri, Q1)};
|
||||||
|
false ->
|
||||||
|
MQ#mqueue{q = priority_queue:in(Msg, Pri, Q)}
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
||||||
|
MQ1#mqueue{q = priority_queue:in(Msg, Pri, Q)}
|
||||||
|
end.
|
||||||
|
|
||||||
out(MQ = #mqueue{len = 0}) ->
|
out(MQ = #mqueue{type = simple, len = 0}) ->
|
||||||
{empty, MQ};
|
{empty, MQ};
|
||||||
|
out(MQ = #mqueue{type = simple, q = Q, len = Len, max_len = infinity}) ->
|
||||||
out(MQ = #mqueue{q = Q, len = Len}) ->
|
{R, Q2} = queue:out(Q),
|
||||||
{Result, Q2} = queue:out(Q),
|
{R, MQ#mqueue{q = Q2, len = Len - 1}};
|
||||||
{Result, maybe_clear_alarm(MQ#mqueue{q = Q2, len = Len - 1})}.
|
out(MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
||||||
|
{R, Q2} = queue:out(Q),
|
||||||
|
{R, maybe_clear_alarm(MQ#mqueue{q = Q2, len = Len - 1})};
|
||||||
|
out(MQ = #mqueue{type = priority, q = Q}) ->
|
||||||
|
{R, Q2} = priority_queue:out(Q),
|
||||||
|
{R, MQ#mqueue{q = Q2}}.
|
||||||
|
|
||||||
maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun = AlarmFun})
|
maybe_set_alarm(MQ = #mqueue{name = Name, len = Len, high_wm = HighWM, alarm_fun = AlarmFun})
|
||||||
when Len > HighWM ->
|
when Len > HighWM ->
|
||||||
Alarm = #mqtt_alarm{id = list_to_binary(["queue_high_watermark.", Name]),
|
Alarm = #mqtt_alarm{id = iolist_to_binary(["queue_high_watermark.", Name]),
|
||||||
severity = warning,
|
severity = warning,
|
||||||
title = io_lib:format("Queue ~s high-water mark", [Name]),
|
title = io_lib:format("Queue ~s high-water mark", [Name]),
|
||||||
summary = io_lib:format("queue len ~p > high_watermark ~p", [Len, HighWM])},
|
summary = io_lib:format("queue len ~p > high_watermark ~p", [Len, HighWM])},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -53,10 +53,7 @@ start_link() ->
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
-spec start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}.
|
-spec start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}.
|
||||||
start_link(Pool, Id) ->
|
start_link(Pool, Id) ->
|
||||||
gen_server:start_link({local, name(Id)}, ?MODULE, [Pool, Id], []).
|
gen_server:start_link({local, emqttd:reg_name(?MODULE, Id)}, ?MODULE, [Pool, Id], []).
|
||||||
|
|
||||||
name(Id) ->
|
|
||||||
list_to_atom(lists:concat([?MODULE, "_", integer_to_list(Id)])).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc Submit work to pooler
|
%% @doc Submit work to pooler
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc emqttd pubsub
|
%%% @doc PubSub
|
||||||
%%%
|
%%%
|
||||||
%%% @author Feng Lee <feng@emqtt.io>
|
%%% @author Feng Lee <feng@emqtt.io>
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
|
@ -43,9 +43,7 @@
|
||||||
-export([start_link/4]).
|
-export([start_link/4]).
|
||||||
|
|
||||||
-export([create/2, lookup/2, subscribe/1, subscribe/2,
|
-export([create/2, lookup/2, subscribe/1, subscribe/2,
|
||||||
unsubscribe/1, unsubscribe/2, publish/1, delete/2]).
|
publish/1, unsubscribe/1, unsubscribe/2, delete/2]).
|
||||||
|
|
||||||
%% Subscriptions API
|
|
||||||
|
|
||||||
%% Local node
|
%% Local node
|
||||||
-export([match/1]).
|
-export([match/1]).
|
||||||
|
@ -62,8 +60,6 @@
|
||||||
|
|
||||||
-define(ROUTER, emqttd_router).
|
-define(ROUTER, emqttd_router).
|
||||||
|
|
||||||
-define(HELPER, emqttd_pubsub_helper).
|
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% Mnesia callbacks
|
%%% Mnesia callbacks
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
@ -123,26 +119,19 @@ cache_env(Key) ->
|
||||||
%%% API
|
%%% API
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Start one pubsub server
|
%% @doc Start one pubsub server
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec start_link(Pool, Id, StatsFun, Opts) -> {ok, pid()} | ignore | {error, any()} when
|
-spec start_link(Pool, Id, StatsFun, Opts) -> {ok, pid()} | ignore | {error, any()} when
|
||||||
Pool :: atom(),
|
Pool :: atom(),
|
||||||
Id :: pos_integer(),
|
Id :: pos_integer(),
|
||||||
StatsFun :: fun(),
|
StatsFun :: fun((atom()) -> any()),
|
||||||
Opts :: list(tuple()).
|
Opts :: list(tuple()).
|
||||||
start_link(Pool, Id, StatsFun, Opts) ->
|
start_link(Pool, Id, StatsFun, Opts) ->
|
||||||
gen_server2:start_link({local, name(Id)}, ?MODULE, [Pool, Id, StatsFun, Opts], []).
|
gen_server2:start_link({local, emqttd:reg_name(?MODULE, Id)},
|
||||||
|
?MODULE, [Pool, Id, StatsFun, Opts], []).
|
||||||
|
|
||||||
name(Id) ->
|
|
||||||
list_to_atom("emqttd_pubsub_" ++ integer_to_list(Id)).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Create Topic or Subscription.
|
%% @doc Create Topic or Subscription.
|
||||||
%% @end
|
-spec create(topic, emqttd_topic:topic()) -> ok | {error, any()};
|
||||||
%%------------------------------------------------------------------------------
|
(subscription, {binary(), binary(), mqtt_qos()}) -> ok | {error, any()}.
|
||||||
-spec create(topic | subscription, binary() | {binary(), binary(), mqtt_qos()}) -> ok | {error, any()}.
|
|
||||||
create(topic, Topic) when is_binary(Topic) ->
|
create(topic, Topic) when is_binary(Topic) ->
|
||||||
Record = #mqtt_topic{topic = Topic, node = node()},
|
Record = #mqtt_topic{topic = Topic, node = node()},
|
||||||
case mnesia:transaction(fun add_topic/1, [Record]) of
|
case mnesia:transaction(fun add_topic/1, [Record]) of
|
||||||
|
@ -151,39 +140,33 @@ create(topic, Topic) when is_binary(Topic) ->
|
||||||
end;
|
end;
|
||||||
|
|
||||||
create(subscription, {SubId, Topic, Qos}) when is_binary(SubId) andalso is_binary(Topic) ->
|
create(subscription, {SubId, Topic, Qos}) when is_binary(SubId) andalso is_binary(Topic) ->
|
||||||
case mnesia:transaction(fun add_subscription/2, [SubId, {Topic, Qos}]) of
|
case mnesia:transaction(fun add_subscription/2, [SubId, {Topic, ?QOS_I(Qos)}]) of
|
||||||
{atomic, ok} -> ok;
|
{atomic, ok} -> ok;
|
||||||
{aborted, Error} -> {error, Error}
|
{aborted, Error} -> {error, Error}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Lookup Topic or Subscription.
|
%% @doc Lookup Topic or Subscription.
|
||||||
%% @end
|
-spec lookup(topic, emqttd_topic:topic()) -> list(mqtt_topic());
|
||||||
%%------------------------------------------------------------------------------
|
(subscription, binary()) -> list(mqtt_subscription()).
|
||||||
-spec lookup(topic | subscription, binary()) -> list().
|
lookup(topic, Topic) when is_binary(Topic) ->
|
||||||
lookup(topic, Topic) ->
|
|
||||||
mnesia:dirty_read(topic, Topic);
|
mnesia:dirty_read(topic, Topic);
|
||||||
|
|
||||||
lookup(subscription, ClientId) ->
|
lookup(subscription, SubId) when is_binary(SubId) ->
|
||||||
mnesia:dirty_read(subscription, ClientId).
|
mnesia:dirty_read(subscription, SubId).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Delete Topic or Subscription.
|
%% @doc Delete Topic or Subscription.
|
||||||
%% @end
|
-spec delete(topic, emqttd_topic:topic()) -> ok | {error, any()};
|
||||||
%%------------------------------------------------------------------------------
|
(subscription, binary() | {binary(), emqttd_topic:topic()}) -> ok.
|
||||||
delete(topic, _Topic) ->
|
delete(topic, _Topic) ->
|
||||||
{error, unsupported};
|
{error, unsupported};
|
||||||
|
|
||||||
delete(subscription, ClientId) when is_binary(ClientId) ->
|
delete(subscription, SubId) when is_binary(SubId) ->
|
||||||
mnesia:dirty_delete({subscription, ClientId});
|
mnesia:dirty_delete({subscription, SubId});
|
||||||
|
|
||||||
delete(subscription, {ClientId, Topic}) when is_binary(ClientId) ->
|
delete(subscription, {SubId, Topic}) when is_binary(SubId) andalso is_binary(Topic) ->
|
||||||
mnesia:async_dirty(fun remove_subscriptions/2, [ClientId, [Topic]]).
|
mnesia:async_dirty(fun remove_subscriptions/2, [SubId, [Topic]]).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Subscribe Topics
|
%% @doc Subscribe Topics
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec subscribe({Topic, Qos} | list({Topic, Qos})) ->
|
-spec subscribe({Topic, Qos} | list({Topic, Qos})) ->
|
||||||
{ok, Qos | list(Qos)} | {error, any()} when
|
{ok, Qos | list(Qos)} | {error, any()} when
|
||||||
Topic :: binary(),
|
Topic :: binary(),
|
||||||
|
@ -206,34 +189,28 @@ subscribe(ClientId, TopicTable) when is_binary(ClientId) andalso is_list(TopicTa
|
||||||
fixqos(TopicTable) ->
|
fixqos(TopicTable) ->
|
||||||
[{Topic, ?QOS_I(Qos)} || {Topic, Qos} <- TopicTable].
|
[{Topic, ?QOS_I(Qos)} || {Topic, Qos} <- TopicTable].
|
||||||
|
|
||||||
call(Request) ->
|
|
||||||
PubSub = gproc_pool:pick_worker(pubsub, self()),
|
|
||||||
gen_server2:call(PubSub, Request, infinity).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Unsubscribe Topic or Topics
|
%% @doc Unsubscribe Topic or Topics
|
||||||
%% @end
|
-spec unsubscribe(emqttd_topic:topic() | list(emqttd_topic:topic())) -> ok.
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec unsubscribe(binary() | list(binary())) -> ok.
|
|
||||||
unsubscribe(Topic) when is_binary(Topic) ->
|
unsubscribe(Topic) when is_binary(Topic) ->
|
||||||
unsubscribe([Topic]);
|
unsubscribe([Topic]);
|
||||||
unsubscribe(Topics = [Topic|_]) when is_binary(Topic) ->
|
unsubscribe(Topics = [Topic|_]) when is_binary(Topic) ->
|
||||||
cast({unsubscribe, {undefined, self()}, Topics}).
|
cast({unsubscribe, {undefined, self()}, Topics}).
|
||||||
|
|
||||||
-spec unsubscribe(binary(), binary() | list(binary())) -> ok.
|
-spec unsubscribe(binary(), emqttd_topic:topic() | list(emqttd_topic:topic())) -> ok.
|
||||||
unsubscribe(ClientId, Topic) when is_binary(ClientId) andalso is_binary(Topic) ->
|
unsubscribe(ClientId, Topic) when is_binary(ClientId) andalso is_binary(Topic) ->
|
||||||
unsubscribe(ClientId, [Topic]);
|
unsubscribe(ClientId, [Topic]);
|
||||||
unsubscribe(ClientId, Topics = [Topic|_]) when is_binary(Topic) ->
|
unsubscribe(ClientId, Topics = [Topic|_]) when is_binary(Topic) ->
|
||||||
cast({unsubscribe, {ClientId, self()}, Topics}).
|
cast({unsubscribe, {ClientId, self()}, Topics}).
|
||||||
|
|
||||||
cast(Msg) ->
|
call(Request) ->
|
||||||
PubSub = gproc_pool:pick_worker(pubsub, self()),
|
gen_server2:call(pick(self()), Request, infinity).
|
||||||
gen_server2:cast(PubSub, Msg).
|
|
||||||
|
cast(Msg) ->
|
||||||
|
gen_server2:cast(pick(self()), Msg).
|
||||||
|
|
||||||
|
pick(Self) -> gproc_pool:pick_worker(pubsub, Self).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Publish to cluster nodes
|
%% @doc Publish to cluster nodes
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec publish(Msg :: mqtt_message()) -> ok.
|
-spec publish(Msg :: mqtt_message()) -> ok.
|
||||||
publish(Msg = #mqtt_message{from = From}) ->
|
publish(Msg = #mqtt_message{from = From}) ->
|
||||||
trace(publish, From, Msg),
|
trace(publish, From, Msg),
|
||||||
|
@ -257,35 +234,41 @@ publish(To, Msg) ->
|
||||||
end
|
end
|
||||||
end, match(To)).
|
end, match(To)).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Match Topic Name with Topic Filters
|
%% @doc Match Topic Name with Topic Filters
|
||||||
%% @end
|
-spec match(emqttd_topic:topic()) -> [mqtt_topic()].
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec match(binary()) -> [mqtt_topic()].
|
|
||||||
match(To) ->
|
match(To) ->
|
||||||
MatchedTopics = mnesia:async_dirty(fun emqttd_trie:match/1, [To]),
|
MatchedTopics = mnesia:async_dirty(fun emqttd_trie:match/1, [To]),
|
||||||
%% ets:lookup for topic table will be replicated.
|
%% ets:lookup for topic table will be replicated to all nodes.
|
||||||
lists:append([ets:lookup(topic, Topic) || Topic <- MatchedTopics]).
|
lists:append([ets:lookup(topic, Topic) || Topic <- MatchedTopics]).
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
init([Pool, Id, StatsFun, Opts]) ->
|
init([Pool, Id, StatsFun, _Opts]) ->
|
||||||
?ROUTER:init(Opts),
|
|
||||||
?GPROC_POOL(join, Pool, Id),
|
?GPROC_POOL(join, Pool, Id),
|
||||||
{ok, #state{pool = Pool, id = Id, statsfun = StatsFun}}.
|
{ok, #state{pool = Pool, id = Id, statsfun = StatsFun}}.
|
||||||
|
|
||||||
handle_call({subscribe, {SubId, SubPid}, TopicTable}, _From,
|
handle_call({subscribe, {SubId, SubPid}, TopicTable}, _From,
|
||||||
State = #state{statsfun = StatsFun}) ->
|
State = #state{statsfun = StatsFun}) ->
|
||||||
|
|
||||||
|
%% Monitor SubPid first
|
||||||
|
try_monitor(SubPid),
|
||||||
|
|
||||||
|
%% Topics
|
||||||
Topics = [Topic || {Topic, _Qos} <- TopicTable],
|
Topics = [Topic || {Topic, _Qos} <- TopicTable],
|
||||||
|
|
||||||
%% Add routes first
|
NewTopics = Topics -- reverse_routes(SubPid),
|
||||||
?ROUTER:add_routes(Topics, SubPid),
|
|
||||||
|
|
||||||
%% Insert topic records to global topic table
|
%% Add routes
|
||||||
Records = [#mqtt_topic{topic = Topic, node = node()} || Topic <- Topics],
|
?ROUTER:add_routes(NewTopics, SubPid),
|
||||||
|
|
||||||
|
insert_reverse_routes(SubPid, NewTopics),
|
||||||
|
|
||||||
|
StatsFun(reverse_route),
|
||||||
|
|
||||||
|
%% Insert topic records to mnesia
|
||||||
|
Records = [#mqtt_topic{topic = Topic, node = node()} || Topic <- NewTopics],
|
||||||
|
|
||||||
case mnesia:transaction(fun add_topics/1, [Records]) of
|
case mnesia:transaction(fun add_topics/1, [Records]) of
|
||||||
{atomic, _} ->
|
{atomic, _} ->
|
||||||
|
@ -307,9 +290,14 @@ handle_call(Req, _From, State) ->
|
||||||
?UNEXPECTED_REQ(Req, State).
|
?UNEXPECTED_REQ(Req, State).
|
||||||
|
|
||||||
handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = StatsFun}) ->
|
handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = StatsFun}) ->
|
||||||
|
|
||||||
%% Delete routes first
|
%% Delete routes first
|
||||||
?ROUTER:delete_routes(Topics, SubPid),
|
?ROUTER:delete_routes(Topics, SubPid),
|
||||||
|
|
||||||
|
delete_reverse_routes(SubPid, Topics),
|
||||||
|
|
||||||
|
StatsFun(reverse_route),
|
||||||
|
|
||||||
%% Remove subscriptions
|
%% Remove subscriptions
|
||||||
if_subscription(
|
if_subscription(
|
||||||
fun(_) ->
|
fun(_) ->
|
||||||
|
@ -317,19 +305,21 @@ handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = St
|
||||||
emqttd_pooler:async_submit({mnesia, async_dirty, Args}),
|
emqttd_pooler:async_submit({mnesia, async_dirty, Args}),
|
||||||
StatsFun(subscription)
|
StatsFun(subscription)
|
||||||
end),
|
end),
|
||||||
|
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
handle_cast(Msg, State) ->
|
handle_cast(Msg, State) ->
|
||||||
?UNEXPECTED_MSG(Msg, State).
|
?UNEXPECTED_MSG(Msg, State).
|
||||||
|
|
||||||
handle_info({'DOWN', _Mon, _Type, DownPid, _Info}, State) ->
|
handle_info({'DOWN', _Mon, _Type, DownPid, _Info}, State = #state{statsfun = StatsFun}) ->
|
||||||
|
|
||||||
Routes = ?ROUTER:lookup_routes(DownPid),
|
Topics = reverse_routes(DownPid),
|
||||||
|
|
||||||
%% Delete all routes of the process
|
?ROUTER:delete_routes(Topics, DownPid),
|
||||||
?ROUTER:delete_routes(DownPid),
|
|
||||||
|
|
||||||
?HELPER:aging([Topic || Topic <- Routes, not ?ROUTER:has_route(Topic)]),
|
delete_reverse_routes(DownPid),
|
||||||
|
|
||||||
|
StatsFun(reverse_route),
|
||||||
|
|
||||||
{noreply, State, hibernate};
|
{noreply, State, hibernate};
|
||||||
|
|
||||||
|
@ -395,6 +385,31 @@ remove_subscriptions(SubId, Topics) ->
|
||||||
delete_subscription(Record) ->
|
delete_subscription(Record) ->
|
||||||
mnesia:delete_object(subscription, Record, write).
|
mnesia:delete_object(subscription, Record, write).
|
||||||
|
|
||||||
|
reverse_routes(SubPid) ->
|
||||||
|
case ets:member(reverse_route, SubPid) of
|
||||||
|
true ->
|
||||||
|
try ets:lookup_element(reverse_route, SubPid, 2) catch error:badarg -> [] end;
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
insert_reverse_routes(SubPid, Topics) ->
|
||||||
|
ets:insert(reverse_route, [{SubPid, Topic} || Topic <- Topics]).
|
||||||
|
|
||||||
|
delete_reverse_routes(SubPid, Topics) ->
|
||||||
|
lists:foreach(fun(Topic) ->
|
||||||
|
ets:delete_object(reverse_route, {SubPid, Topic})
|
||||||
|
end, Topics).
|
||||||
|
|
||||||
|
delete_reverse_routes(SubPid) ->
|
||||||
|
ets:delete(reverse_route, SubPid).
|
||||||
|
|
||||||
|
try_monitor(SubPid) ->
|
||||||
|
case ets:member(reverse_route, SubPid) of
|
||||||
|
true -> ignore;
|
||||||
|
false -> erlang:monitor(process, SubPid)
|
||||||
|
end.
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% Trace Functions
|
%%% Trace Functions
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -19,176 +19,66 @@
|
||||||
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc PubSub Route Aging Helper
|
%%% @doc PubSub Helper.
|
||||||
%%%
|
%%%
|
||||||
%%% @author Feng Lee <feng@emqtt.io>
|
%%% @author Feng Lee <feng@emqtt.io>
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-module(emqttd_pubsub_helper).
|
-module(emqttd_pubsub_helper).
|
||||||
|
|
||||||
-behaviour(gen_server2).
|
-behaviour(gen_server).
|
||||||
|
|
||||||
-include("emqttd.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqttd_internal.hrl").
|
-include("emqttd_internal.hrl").
|
||||||
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start_link/2, aging/1]).
|
-export([start_link/1]).
|
||||||
|
|
||||||
%% gen_server Function Exports
|
%% gen_server Function Exports
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
terminate/2, code_change/3]).
|
terminate/2, code_change/3]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-record(state, {statsfun}).
|
||||||
-compile(export_all).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
-record(aging, {topics, time, tref}).
|
|
||||||
|
|
||||||
-record(state, {aging :: #aging{}, statsfun}).
|
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
-define(ROUTER, emqttd_router).
|
%% @doc Start PubSub Helper.
|
||||||
|
-spec start_link(fun()) -> {ok, pid()} | ignore | {error, any()}.
|
||||||
%%%=============================================================================
|
start_link(StatsFun) ->
|
||||||
%%% API
|
gen_server:start_link({local, ?SERVER}, ?MODULE, [StatsFun], []).
|
||||||
%%%=============================================================================
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Start pubsub helper.
|
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec start_link(fun(), list(tuple())) -> {ok, pid()} | ignore | {error, any()}.
|
|
||||||
start_link(StatsFun, Opts) ->
|
|
||||||
gen_server2:start_link({local, ?SERVER}, ?MODULE, [StatsFun, Opts], []).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Aging topics
|
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec aging(list(binary())) -> ok.
|
|
||||||
aging(Topics) ->
|
|
||||||
gen_server2:cast(?SERVER, {aging, Topics}).
|
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
init([StatsFun, Opts]) ->
|
init([StatsFun]) ->
|
||||||
mnesia:subscribe(system),
|
mnesia:subscribe(system),
|
||||||
|
{ok, #state{statsfun = StatsFun}}.
|
||||||
AgingSecs = proplists:get_value(route_aging, Opts, 5),
|
|
||||||
|
|
||||||
%% Aging Timer
|
|
||||||
{ok, AgingTref} = start_tick(AgingSecs div 2),
|
|
||||||
|
|
||||||
{ok, #state{aging = #aging{topics = dict:new(),
|
|
||||||
time = AgingSecs,
|
|
||||||
tref = AgingTref},
|
|
||||||
statsfun = StatsFun}}.
|
|
||||||
|
|
||||||
start_tick(Secs) ->
|
|
||||||
timer:send_interval(timer:seconds(Secs), {clean, aged}).
|
|
||||||
|
|
||||||
handle_call(Req, _From, State) ->
|
handle_call(Req, _From, State) ->
|
||||||
?UNEXPECTED_REQ(Req, State).
|
?UNEXPECTED_REQ(Req, State).
|
||||||
|
|
||||||
handle_cast({aging, Topics}, State = #state{aging = Aging}) ->
|
|
||||||
#aging{topics = Dict} = Aging,
|
|
||||||
TS = emqttd_util:now_to_secs(),
|
|
||||||
Dict1 =
|
|
||||||
lists:foldl(fun(Topic, Acc) ->
|
|
||||||
case dict:find(Topic, Acc) of
|
|
||||||
{ok, _} -> Acc;
|
|
||||||
error -> dict:store(Topic, TS, Acc)
|
|
||||||
end
|
|
||||||
end, Dict, Topics),
|
|
||||||
{noreply, State#state{aging = Aging#aging{topics = Dict1}}};
|
|
||||||
|
|
||||||
handle_cast(Msg, State) ->
|
handle_cast(Msg, State) ->
|
||||||
?UNEXPECTED_MSG(Msg, State).
|
?UNEXPECTED_MSG(Msg, State).
|
||||||
|
|
||||||
handle_info({clean, aged}, State = #state{aging = Aging}) ->
|
|
||||||
|
|
||||||
#aging{topics = Dict, time = Time} = Aging,
|
|
||||||
|
|
||||||
ByTime = emqttd_util:now_to_secs() - Time,
|
|
||||||
|
|
||||||
Dict1 = try_clean(ByTime, dict:to_list(Dict)),
|
|
||||||
|
|
||||||
NewAging = Aging#aging{topics = dict:from_list(Dict1)},
|
|
||||||
|
|
||||||
noreply(State#state{aging = NewAging});
|
|
||||||
|
|
||||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||||
%% mnesia master?
|
%% TODO: mnesia master?
|
||||||
Pattern = #mqtt_topic{_ = '_', node = Node},
|
Pattern = #mqtt_topic{_ = '_', node = Node},
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
[mnesia:delete_object(topic, R, write) ||
|
[mnesia:delete_object(topic, R, write) ||
|
||||||
R <- mnesia:match_object(topic, Pattern, write)]
|
R <- mnesia:match_object(topic, Pattern, write)]
|
||||||
end,
|
end,
|
||||||
mnesia:async_dirty(F),
|
mnesia:transaction(F), noreply(State);
|
||||||
noreply(State);
|
|
||||||
|
|
||||||
handle_info(Info, State) ->
|
handle_info(Info, State) ->
|
||||||
?UNEXPECTED_INFO(Info, State).
|
?UNEXPECTED_INFO(Info, State).
|
||||||
|
|
||||||
terminate(_Reason, #state{aging = #aging{tref = TRef}}) ->
|
terminate(_Reason, _State) ->
|
||||||
timer:cancel(TRef).
|
mnesia:unsubscribe(system).
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
%%%=============================================================================
|
|
||||||
%%% Internal Functions
|
|
||||||
%%%=============================================================================
|
|
||||||
|
|
||||||
noreply(State = #state{statsfun = StatsFun}) ->
|
noreply(State = #state{statsfun = StatsFun}) ->
|
||||||
StatsFun(topic),
|
StatsFun(topic), {noreply, State}.
|
||||||
{noreply, State, hibernate}.
|
|
||||||
|
|
||||||
try_clean(ByTime, List) ->
|
|
||||||
try_clean(ByTime, List, []).
|
|
||||||
|
|
||||||
try_clean(_ByTime, [], Acc) ->
|
|
||||||
Acc;
|
|
||||||
|
|
||||||
try_clean(ByTime, [{Topic, TS} | Left], Acc) ->
|
|
||||||
case ?ROUTER:has_route(Topic) of
|
|
||||||
false ->
|
|
||||||
try_clean2(ByTime, {Topic, TS}, Left, Acc);
|
|
||||||
true ->
|
|
||||||
try_clean(ByTime, Left, Acc)
|
|
||||||
end.
|
|
||||||
|
|
||||||
try_clean2(ByTime, {Topic, TS}, Left, Acc) when TS > ByTime ->
|
|
||||||
try_clean(ByTime, Left, [{Topic, TS}|Acc]);
|
|
||||||
|
|
||||||
try_clean2(ByTime, {Topic, _TS}, Left, Acc) ->
|
|
||||||
TopicR = #mqtt_topic{topic = Topic, node = node()},
|
|
||||||
mnesia:transaction(fun try_remove_topic/1, [TopicR]),
|
|
||||||
try_clean(ByTime, Left, Acc).
|
|
||||||
|
|
||||||
try_remove_topic(TopicR = #mqtt_topic{topic = Topic}) ->
|
|
||||||
%% Lock topic first
|
|
||||||
case mnesia:wread({topic, Topic}) of
|
|
||||||
[] -> ok;
|
|
||||||
[TopicR] ->
|
|
||||||
if_no_route(Topic, fun() ->
|
|
||||||
%% Remove topic and trie
|
|
||||||
mnesia:delete_object(topic, TopicR, write),
|
|
||||||
emqttd_trie:delete(Topic)
|
|
||||||
end);
|
|
||||||
_More ->
|
|
||||||
if_no_route(Topic, fun() ->
|
|
||||||
%% Remove topic
|
|
||||||
mnesia:delete_object(topic, TopicR, write)
|
|
||||||
end)
|
|
||||||
end.
|
|
||||||
|
|
||||||
if_no_route(Topic, Fun) ->
|
|
||||||
case ?ROUTER:has_route(Topic) of
|
|
||||||
true -> ok;
|
|
||||||
false -> Fun()
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc PubSub Supervisor
|
%%% @doc PubSub Supervisor.
|
||||||
%%%
|
%%%
|
||||||
%%% @author Feng Lee <feng@emqtt.io>
|
%%% @author Feng Lee <feng@emqtt.io>
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
|
@ -31,8 +31,10 @@
|
||||||
|
|
||||||
-define(HELPER, emqttd_pubsub_helper).
|
-define(HELPER, emqttd_pubsub_helper).
|
||||||
|
|
||||||
|
-define(CONCURRENCY_OPTS, [{read_concurrency, true}, {write_concurrency, true}]).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/0]).
|
-export([start_link/0, pubsub_pool/0]).
|
||||||
|
|
||||||
%% Supervisor callbacks
|
%% Supervisor callbacks
|
||||||
-export([init/1]).
|
-export([init/1]).
|
||||||
|
@ -40,24 +42,57 @@
|
||||||
start_link() ->
|
start_link() ->
|
||||||
supervisor:start_link({local, ?MODULE}, ?MODULE, [emqttd_broker:env(pubsub)]).
|
supervisor:start_link({local, ?MODULE}, ?MODULE, [emqttd_broker:env(pubsub)]).
|
||||||
|
|
||||||
init([Opts]) ->
|
pubsub_pool() ->
|
||||||
|
hd([Pid|| {pubsub_pool, Pid, _, _} <- supervisor:which_children(?MODULE)]).
|
||||||
|
|
||||||
|
init([Env]) ->
|
||||||
|
%% Create tabs
|
||||||
|
create_tab(route), create_tab(reverse_route),
|
||||||
|
|
||||||
%% PubSub Helper
|
%% PubSub Helper
|
||||||
Helper = {helper, {?HELPER, start_link, [fun stats/1, Opts]},
|
Helper = {helper, {?HELPER, start_link, [fun setstats/1]},
|
||||||
permanent, infinity, worker, [?HELPER]},
|
permanent, infinity, worker, [?HELPER]},
|
||||||
|
|
||||||
|
%% Router Pool Sup
|
||||||
|
RouterMFA = {emqttd_router, start_link, [fun setstats/1, Env]},
|
||||||
|
%% Pool_size / 2
|
||||||
|
RouterSup = emqttd_pool_sup:spec(router_pool, [router, hash, pool_size(Env) div 2, RouterMFA]),
|
||||||
|
|
||||||
%% PubSub Pool Sup
|
%% PubSub Pool Sup
|
||||||
MFA = {emqttd_pubsub, start_link, [fun stats/1, Opts]},
|
PubSubMFA = {emqttd_pubsub, start_link, [fun setstats/1, Env]},
|
||||||
PoolSup = emqttd_pool_sup:spec([pubsub, hash, pool_size(Opts), MFA]),
|
PubSubSup = emqttd_pool_sup:spec(pubsub_pool, [pubsub, hash, pool_size(Env), PubSubMFA]),
|
||||||
{ok, {{one_for_all, 10, 60}, [Helper, PoolSup]}}.
|
|
||||||
|
|
||||||
pool_size(Opts) ->
|
{ok, {{one_for_all, 10, 60}, [Helper, RouterSup, PubSubSup]}}.
|
||||||
|
|
||||||
|
create_tab(route) ->
|
||||||
|
%% Route Table: Topic -> Pid1, Pid2, ..., PidN
|
||||||
|
%% duplicate_bag: o(1) insert
|
||||||
|
ensure_tab(route, [public, named_table, duplicate_bag | ?CONCURRENCY_OPTS]);
|
||||||
|
|
||||||
|
create_tab(reverse_route) ->
|
||||||
|
%% Reverse Route Table: Pid -> Topic1, Topic2, ..., TopicN
|
||||||
|
ensure_tab(reverse_route, [public, named_table, bag | ?CONCURRENCY_OPTS]).
|
||||||
|
|
||||||
|
ensure_tab(Tab, Opts) ->
|
||||||
|
case ets:info(Tab, name) of
|
||||||
|
undefined -> ets:new(Tab, Opts);
|
||||||
|
_ -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
pool_size(Env) ->
|
||||||
Schedulers = erlang:system_info(schedulers),
|
Schedulers = erlang:system_info(schedulers),
|
||||||
proplists:get_value(pool_size, Opts, Schedulers).
|
proplists:get_value(pool_size, Env, Schedulers).
|
||||||
|
|
||||||
stats(topic) ->
|
setstats(route) ->
|
||||||
emqttd_stats:setstats('topics/count', 'topics/max',
|
emqttd_stats:setstat('routes/count', ets:info(route, size));
|
||||||
mnesia:table_info(topic, size));
|
|
||||||
stats(subscription) ->
|
setstats(reverse_route) ->
|
||||||
|
emqttd_stats:setstat('routes/reverse', ets:info(reverse_route, size));
|
||||||
|
|
||||||
|
setstats(topic) ->
|
||||||
|
emqttd_stats:setstats('topics/count', 'topics/max', mnesia:table_info(topic, size));
|
||||||
|
|
||||||
|
setstats(subscription) ->
|
||||||
emqttd_stats:setstats('subscriptions/count', 'subscriptions/max',
|
emqttd_stats:setstats('subscriptions/count', 'subscriptions/max',
|
||||||
mnesia:table_info(subscription, size)).
|
mnesia:table_info(subscription, size)).
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -19,167 +19,267 @@
|
||||||
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
%%% SOFTWARE.
|
%%% SOFTWARE.
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% @doc MQTT Message Router on Local Node
|
%%% @doc Message Router on local node.
|
||||||
%%%
|
|
||||||
%%% Route Table:
|
|
||||||
%%%
|
|
||||||
%%% Topic -> Pid1, Pid2, ...
|
|
||||||
%%%
|
|
||||||
%%% Reverse Route Table:
|
|
||||||
%%%
|
|
||||||
%%% Pid -> Topic1, Topic2, ...
|
|
||||||
%%%
|
|
||||||
%%% @end
|
|
||||||
%%%
|
%%%
|
||||||
%%% @author Feng Lee <feng@emqtt.io>
|
%%% @author Feng Lee <feng@emqtt.io>
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
|
|
||||||
-module(emqttd_router).
|
-module(emqttd_router).
|
||||||
|
|
||||||
|
-behaviour(gen_server2).
|
||||||
|
|
||||||
-include("emqttd.hrl").
|
-include("emqttd.hrl").
|
||||||
|
|
||||||
-include("emqttd_protocol.hrl").
|
-include("emqttd_protocol.hrl").
|
||||||
|
|
||||||
-export([init/1, route/2, lookup_routes/1, has_route/1,
|
-include("emqttd_internal.hrl").
|
||||||
add_routes/2, delete_routes/1, delete_routes/2]).
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
-export([start_link/4]).
|
||||||
-compile(export_all).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%% Route API
|
||||||
%% @doc Create route tables.
|
-export([route/2]).
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
init(_Opts) ->
|
|
||||||
TabOpts = [bag, public, named_table,
|
|
||||||
{write_concurrency, true}],
|
|
||||||
%% Route Table: Topic -> {Pid, QoS}
|
|
||||||
%% Route Shard: {Topic, Shard} -> {Pid, QoS}
|
|
||||||
ensure_tab(route, TabOpts),
|
|
||||||
|
|
||||||
%% Reverse Route Table: Pid -> {Topic, QoS}
|
%% Route Admin API
|
||||||
ensure_tab(reverse_route, TabOpts).
|
-export([add_route/2, lookup_routes/1, has_route/1, delete_route/2]).
|
||||||
|
|
||||||
ensure_tab(Tab, Opts) ->
|
%% Batch API
|
||||||
case ets:info(Tab, name) of
|
-export([add_routes/2, delete_routes/2]).
|
||||||
undefined ->
|
|
||||||
ets:new(Tab, Opts);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
%% For Test
|
||||||
destory() ->
|
-export([stop/1]).
|
||||||
ets:delete(route),
|
|
||||||
ets:delete(reverse_route).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%% gen_server Callbacks
|
||||||
%% @doc Add Routes.
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
%% @end
|
terminate/2, code_change/3]).
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec add_routes(list(binary()), pid()) -> ok.
|
|
||||||
add_routes(Topics, Pid) when is_pid(Pid) ->
|
|
||||||
with_stats(fun() ->
|
|
||||||
case lookup_routes(Pid) of
|
|
||||||
[] ->
|
|
||||||
erlang:monitor(process, Pid),
|
|
||||||
insert_routes(Topics, Pid);
|
|
||||||
InEts ->
|
|
||||||
insert_routes(Topics -- InEts, Pid)
|
|
||||||
end
|
|
||||||
end).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
-record(aging, {topics, time, tref}).
|
||||||
%% @doc Lookup Routes
|
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec lookup_routes(pid()) -> list(binary()).
|
|
||||||
lookup_routes(Pid) when is_pid(Pid) ->
|
|
||||||
[Topic || {_, Topic} <- ets:lookup(reverse_route, Pid)].
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
-record(state, {pool, id, statsfun, aging :: #aging{}}).
|
||||||
%% @doc Has Route?
|
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec has_route(binary()) -> boolean().
|
|
||||||
has_route(Topic) ->
|
|
||||||
ets:member(route, Topic).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%% @doc Start a local router.
|
||||||
%% @doc Delete Routes
|
-spec start_link(atom(), pos_integer(), fun((atom()) -> ok), list()) -> {ok, pid()} | {error, any()}.
|
||||||
%% @end
|
start_link(Pool, Id, StatsFun, Env) ->
|
||||||
%%------------------------------------------------------------------------------
|
gen_server2:start_link({local, emqttd:reg_name(?MODULE,Id)},
|
||||||
-spec delete_routes(list(binary()), pid()) -> ok.
|
?MODULE, [Pool, Id, StatsFun, Env], []).
|
||||||
delete_routes(Topics, Pid) ->
|
|
||||||
with_stats(fun() ->
|
|
||||||
Routes = [{Topic, Pid} || Topic <- Topics],
|
|
||||||
lists:foreach(fun delete_route/1, Routes)
|
|
||||||
end).
|
|
||||||
|
|
||||||
-spec delete_routes(pid()) -> ok.
|
%% @doc Route Message on the local node.
|
||||||
delete_routes(Pid) when is_pid(Pid) ->
|
-spec route(emqttd_topic:topic(), mqtt_message()) -> any().
|
||||||
with_stats(fun() ->
|
|
||||||
Routes = [{Topic, Pid} || Topic <- lookup_routes(Pid)],
|
|
||||||
ets:delete(reverse_route, Pid),
|
|
||||||
lists:foreach(fun delete_route_only/1, Routes)
|
|
||||||
end).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% @doc Route Message on Local Node.
|
|
||||||
%% @end
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec route(binary(), mqtt_message()) -> non_neg_integer().
|
|
||||||
route(Queue = <<"$Q/", _Q>>, Msg) ->
|
route(Queue = <<"$Q/", _Q>>, Msg) ->
|
||||||
case ets:lookup(route, Queue) of
|
case lookup_routes(Queue) of
|
||||||
[] ->
|
[] ->
|
||||||
emqttd_metrics:inc('messages/dropped');
|
emqttd_metrics:inc('messages/dropped');
|
||||||
|
[SubPid] ->
|
||||||
|
SubPid ! {dispatch, Queue, Msg};
|
||||||
Routes ->
|
Routes ->
|
||||||
Idx = crypto:rand_uniform(1, length(Routes) + 1),
|
Idx = crypto:rand_uniform(1, length(Routes) + 1),
|
||||||
{_, SubPid} = lists:nth(Idx, Routes),
|
SubPid = lists:nth(Idx, Routes),
|
||||||
dispatch(SubPid, Queue, Msg)
|
SubPid ! {dispatch, Queue, Msg}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
route(Topic, Msg) ->
|
route(Topic, Msg) ->
|
||||||
case ets:lookup(route, Topic) of
|
case lookup_routes(Topic) of
|
||||||
[] ->
|
[] ->
|
||||||
emqttd_metrics:inc('messages/dropped');
|
emqttd_metrics:inc('messages/dropped');
|
||||||
|
[SubPid] -> %% optimize
|
||||||
|
SubPid ! {dispatch, Topic, Msg};
|
||||||
Routes ->
|
Routes ->
|
||||||
lists:foreach(fun({_Topic, SubPid}) ->
|
lists:foreach(fun(SubPid) ->
|
||||||
dispatch(SubPid, Topic, Msg)
|
SubPid ! {dispatch, Topic, Msg}
|
||||||
end, Routes)
|
end, Routes)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
dispatch(SubPid, Topic, Msg) -> SubPid ! {dispatch, Topic, Msg}.
|
%% @doc Has Route?
|
||||||
|
-spec has_route(emqttd_topic:topic()) -> boolean().
|
||||||
|
has_route(Topic) ->
|
||||||
|
ets:member(route, Topic).
|
||||||
|
|
||||||
%%%=============================================================================
|
%% @doc Lookup Routes
|
||||||
%%% Internal Functions
|
-spec lookup_routes(emqttd_topic:topic()) -> list(pid()).
|
||||||
%%%=============================================================================
|
lookup_routes(Topic) when is_binary(Topic) ->
|
||||||
|
case ets:member(route, Topic) of
|
||||||
|
true ->
|
||||||
|
try ets:lookup_element(route, Topic, 2) catch error:badarg -> [] end;
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
insert_routes([], _Pid) ->
|
%% @doc Add Route.
|
||||||
|
-spec add_route(emqttd_topic:topic(), pid()) -> ok.
|
||||||
|
add_route(Topic, Pid) when is_pid(Pid) ->
|
||||||
|
call(pick(Topic), {add_route, Topic, Pid}).
|
||||||
|
|
||||||
|
%% @doc Add Routes.
|
||||||
|
-spec add_routes(list(emqttd_topic:topic()), pid()) -> ok.
|
||||||
|
add_routes([], _Pid) ->
|
||||||
ok;
|
ok;
|
||||||
insert_routes(Topics, Pid) ->
|
add_routes([Topic], Pid) ->
|
||||||
{Routes, ReverseRoutes} = routes(Topics, Pid),
|
add_route(Topic, Pid);
|
||||||
ets:insert(route, Routes),
|
|
||||||
ets:insert(reverse_route, ReverseRoutes).
|
|
||||||
|
|
||||||
routes(Topics, Pid) ->
|
add_routes(Topics, Pid) ->
|
||||||
lists:unzip([{{Topic, Pid}, {Pid, Topic}} || Topic <- Topics]).
|
lists:foreach(fun({Router, Slice}) ->
|
||||||
|
call(Router, {add_routes, Slice, Pid})
|
||||||
|
end, slice(Topics)).
|
||||||
|
|
||||||
delete_route({Topic, Pid}) ->
|
%% @doc Delete Route.
|
||||||
ets:delete_object(reverse_route, {Pid, Topic}),
|
-spec delete_route(emqttd_topic:topic(), pid()) -> ok.
|
||||||
ets:delete_object(route, {Topic, Pid}).
|
delete_route(Topic, Pid) ->
|
||||||
|
cast(pick(Topic), {delete_route, Topic, Pid}).
|
||||||
|
|
||||||
delete_route_only({Topic, Pid}) ->
|
%% @doc Delete Routes.
|
||||||
ets:delete_object(route, {Topic, Pid}).
|
-spec delete_routes(list(emqttd_topic:topic()), pid()) -> ok.
|
||||||
|
delete_routes([Topic], Pid) ->
|
||||||
|
delete_route(Topic, Pid);
|
||||||
|
|
||||||
with_stats(Fun) ->
|
delete_routes(Topics, Pid) ->
|
||||||
Ok = Fun(), setstats(), Ok.
|
lists:foreach(fun({Router, Slice}) ->
|
||||||
|
cast(Router, {delete_routes, Slice, Pid})
|
||||||
|
end, slice(Topics)).
|
||||||
|
|
||||||
setstats() ->
|
%% @private Slice topics.
|
||||||
lists:foreach(fun setstat/1, [{route, 'routes/count'},
|
slice(Topics) ->
|
||||||
{reverse_route, 'routes/reverse'}]).
|
dict:to_list(lists:foldl(fun(Topic, Dict) ->
|
||||||
|
dict:append(pick(Topic), Topic, Dict)
|
||||||
|
end, dict:new(), Topics)).
|
||||||
|
|
||||||
setstat({Tab, Stat}) ->
|
%% @private Pick a router.
|
||||||
emqttd_stats:setstat(Stat, ets:info(Tab, size)).
|
pick(Topic) ->
|
||||||
|
gproc_pool:pick_worker(router, Topic).
|
||||||
|
|
||||||
|
stop(Id) when is_integer(Id) ->
|
||||||
|
gen_server2:call(emqttd:reg_name(?MODULE, Id), stop).
|
||||||
|
|
||||||
|
call(Router, Request) ->
|
||||||
|
gen_server2:call(Router, Request, infinity).
|
||||||
|
|
||||||
|
cast(Router, Msg) ->
|
||||||
|
gen_server2:cast(Router, Msg).
|
||||||
|
|
||||||
|
init([Pool, Id, StatsFun, Opts]) ->
|
||||||
|
|
||||||
|
%% Calls from pubsub should be scheduled first?
|
||||||
|
process_flag(priority, high),
|
||||||
|
|
||||||
|
?GPROC_POOL(join, Pool, Id),
|
||||||
|
|
||||||
|
emqttd:seed_now(),
|
||||||
|
|
||||||
|
AgingSecs = proplists:get_value(route_aging, Opts, 5),
|
||||||
|
|
||||||
|
%% Aging Timer
|
||||||
|
{ok, AgingTref} = start_tick(AgingSecs + random:uniform(AgingSecs)),
|
||||||
|
|
||||||
|
Aging = #aging{topics = dict:new(), time = AgingSecs, tref = AgingTref},
|
||||||
|
|
||||||
|
{ok, #state{pool = Pool, id = Id, statsfun = StatsFun, aging = Aging}}.
|
||||||
|
|
||||||
|
start_tick(Secs) ->
|
||||||
|
timer:send_interval(timer:seconds(Secs), {clean, aged}).
|
||||||
|
|
||||||
|
handle_call(stop, _From, State) ->
|
||||||
|
{stop, normal, ok, State};
|
||||||
|
|
||||||
|
handle_call({add_route, Topic, Pid}, _From, State) ->
|
||||||
|
ets:insert(route, {Topic, Pid}),
|
||||||
|
{reply, ok, setstats(State)};
|
||||||
|
|
||||||
|
handle_call({add_routes, Topics, Pid}, _From, State) ->
|
||||||
|
ets:insert(route, [{Topic, Pid} || Topic <- Topics]),
|
||||||
|
{reply, ok, setstats(State)};
|
||||||
|
|
||||||
|
handle_call(Req, _From, State) ->
|
||||||
|
?UNEXPECTED_REQ(Req, State).
|
||||||
|
|
||||||
|
handle_cast({delete_route, Topic, Pid}, State = #state{aging = Aging}) ->
|
||||||
|
ets:delete_object(route, {Topic, Pid}),
|
||||||
|
NewState =
|
||||||
|
case has_route(Topic) of
|
||||||
|
false -> State#state{aging = store_aged(Topic, Aging)};
|
||||||
|
true -> State
|
||||||
|
end,
|
||||||
|
{noreply, setstats(NewState)};
|
||||||
|
|
||||||
|
handle_cast({delete_routes, Topics, Pid}, State) ->
|
||||||
|
NewAging =
|
||||||
|
lists:foldl(fun(Topic, Aging) ->
|
||||||
|
ets:delete_object(route, {Topic, Pid}),
|
||||||
|
case has_route(Topic) of
|
||||||
|
false -> store_aged(Topic, Aging);
|
||||||
|
true -> Aging
|
||||||
|
end
|
||||||
|
end, State#state.aging, Topics),
|
||||||
|
{noreply, setstats(State#state{aging = NewAging})};
|
||||||
|
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
?UNEXPECTED_MSG(Msg, State).
|
||||||
|
|
||||||
|
handle_info({clean, aged}, State = #state{aging = Aging}) ->
|
||||||
|
|
||||||
|
#aging{topics = Dict, time = Time} = Aging,
|
||||||
|
|
||||||
|
ByTime = emqttd_util:now_to_secs() - Time,
|
||||||
|
|
||||||
|
Dict1 = try_clean(ByTime, dict:to_list(Dict)),
|
||||||
|
|
||||||
|
NewAging = Aging#aging{topics = dict:from_list(Dict1)},
|
||||||
|
|
||||||
|
{noreply, State#state{aging = NewAging}, hibernate};
|
||||||
|
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
?UNEXPECTED_INFO(Info, State).
|
||||||
|
|
||||||
|
terminate(_Reason, #state{pool = Pool, id = Id, aging = #aging{tref = TRef}}) ->
|
||||||
|
timer:cancel(TRef),
|
||||||
|
?GPROC_POOL(leave, Pool, Id).
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
try_clean(ByTime, List) ->
|
||||||
|
try_clean(ByTime, List, []).
|
||||||
|
|
||||||
|
try_clean(_ByTime, [], Acc) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
try_clean(ByTime, [{Topic, TS} | Left], Acc) ->
|
||||||
|
case has_route(Topic) of
|
||||||
|
false ->
|
||||||
|
try_clean2(ByTime, {Topic, TS}, Left, Acc);
|
||||||
|
true ->
|
||||||
|
try_clean(ByTime, Left, Acc)
|
||||||
|
end.
|
||||||
|
|
||||||
|
try_clean2(ByTime, {Topic, TS}, Left, Acc) when TS > ByTime ->
|
||||||
|
try_clean(ByTime, Left, [{Topic, TS} | Acc]);
|
||||||
|
|
||||||
|
try_clean2(ByTime, {Topic, _TS}, Left, Acc) ->
|
||||||
|
TopicR = #mqtt_topic{topic = Topic, node = node()},
|
||||||
|
case mnesia:transaction(fun try_remove_topic/1, [TopicR]) of
|
||||||
|
{atomic, _} -> ok;
|
||||||
|
{aborted, Error} -> lager:error("Clean Topic '~s' Error: ~p", [Topic, Error])
|
||||||
|
end,
|
||||||
|
try_clean(ByTime, Left, Acc).
|
||||||
|
|
||||||
|
try_remove_topic(TopicR = #mqtt_topic{topic = Topic}) ->
|
||||||
|
%% Lock topic first
|
||||||
|
case mnesia:wread({topic, Topic}) of
|
||||||
|
[] ->
|
||||||
|
ok; %% mnesia:abort(not_found);
|
||||||
|
[TopicR] ->
|
||||||
|
%% Remove topic and trie
|
||||||
|
delete_topic(TopicR),
|
||||||
|
emqttd_trie:delete(Topic);
|
||||||
|
_More ->
|
||||||
|
%% Remove topic only
|
||||||
|
delete_topic(TopicR)
|
||||||
|
end.
|
||||||
|
|
||||||
|
delete_topic(TopicR) ->
|
||||||
|
mnesia:delete_object(topic, TopicR, write).
|
||||||
|
|
||||||
|
store_aged(Topic, Aging = #aging{topics = Dict}) ->
|
||||||
|
Now = emqttd_util:now_to_secs(),
|
||||||
|
Aging#aging{topics = dict:store(Topic, Now, Dict)}.
|
||||||
|
|
||||||
|
setstats(State = #state{statsfun = StatsFun}) ->
|
||||||
|
StatsFun(route), State.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -85,10 +85,7 @@ mnesia(copy) ->
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}.
|
-spec start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, any()}.
|
||||||
start_link(Pool, Id) ->
|
start_link(Pool, Id) ->
|
||||||
gen_server2:start_link({local, name(Id)}, ?MODULE, [Pool, Id], []).
|
gen_server2:start_link({local, emqttd:reg_name(?MODULE, Id)}, ?MODULE, [Pool, Id], []).
|
||||||
|
|
||||||
name(Id) ->
|
|
||||||
list_to_atom("emqttd_sm_" ++ integer_to_list(Id)).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc Start a session
|
%% @doc Start a session
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -143,7 +143,7 @@ setstats(Stat, MaxStat, Val) ->
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
random:seed(os:timestamp()),
|
emqttd:seed_now(),
|
||||||
ets:new(?STATS_TAB, [set, public, named_table, {write_concurrency, true}]),
|
ets:new(?STATS_TAB, [set, public, named_table, {write_concurrency, true}]),
|
||||||
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
|
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
|
||||||
ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),
|
ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -31,6 +31,8 @@
|
||||||
|
|
||||||
-export([join/1, feed_var/3, is_queue/1, systop/1]).
|
-export([join/1, feed_var/3, is_queue/1, systop/1]).
|
||||||
|
|
||||||
|
-type topic() :: binary().
|
||||||
|
|
||||||
%-type type() :: static | dynamic.
|
%-type type() :: static | dynamic.
|
||||||
|
|
||||||
-type word() :: '' | '+' | '#' | binary().
|
-type word() :: '' | '+' | '#' | binary().
|
||||||
|
@ -39,7 +41,7 @@
|
||||||
|
|
||||||
-type triple() :: {root | binary(), word(), binary()}.
|
-type triple() :: {root | binary(), word(), binary()}.
|
||||||
|
|
||||||
-export_type([word/0, triple/0]).
|
-export_type([topic/0, word/0, triple/0]).
|
||||||
|
|
||||||
-define(MAX_TOPIC_LEN, 4096).
|
-define(MAX_TOPIC_LEN, 4096).
|
||||||
|
|
||||||
|
@ -47,7 +49,7 @@
|
||||||
%% @doc Is wildcard topic?
|
%% @doc Is wildcard topic?
|
||||||
%% @end
|
%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-spec wildcard(binary()) -> true | false.
|
-spec wildcard(topic()) -> true | false.
|
||||||
wildcard(Topic) when is_binary(Topic) ->
|
wildcard(Topic) when is_binary(Topic) ->
|
||||||
wildcard(words(Topic));
|
wildcard(words(Topic));
|
||||||
wildcard([]) ->
|
wildcard([]) ->
|
||||||
|
@ -64,8 +66,8 @@ wildcard([_H|T]) ->
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec match(Name, Filter) -> boolean() when
|
-spec match(Name, Filter) -> boolean() when
|
||||||
Name :: binary() | words(),
|
Name :: topic() | words(),
|
||||||
Filter :: binary() | words().
|
Filter :: topic() | words().
|
||||||
match(Name, Filter) when is_binary(Name) and is_binary(Filter) ->
|
match(Name, Filter) when is_binary(Name) and is_binary(Filter) ->
|
||||||
match(words(Name), words(Filter));
|
match(words(Name), words(Filter));
|
||||||
match([], []) ->
|
match([], []) ->
|
||||||
|
@ -91,7 +93,7 @@ match([], [_H|_T2]) ->
|
||||||
%% @doc Validate Topic
|
%% @doc Validate Topic
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec validate({name | filter, binary()}) -> boolean().
|
-spec validate({name | filter, topic()}) -> boolean().
|
||||||
validate({_, <<>>}) ->
|
validate({_, <<>>}) ->
|
||||||
false;
|
false;
|
||||||
validate({_, Topic}) when is_binary(Topic) and (size(Topic) > ?MAX_TOPIC_LEN) ->
|
validate({_, Topic}) when is_binary(Topic) and (size(Topic) > ?MAX_TOPIC_LEN) ->
|
||||||
|
@ -129,7 +131,7 @@ validate3(<<_/utf8, Rest/binary>>) ->
|
||||||
%% @doc Topic to Triples
|
%% @doc Topic to Triples
|
||||||
%% @end
|
%% @end
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
-spec triples(binary()) -> list(triple()).
|
-spec triples(topic()) -> list(triple()).
|
||||||
triples(Topic) when is_binary(Topic) ->
|
triples(Topic) when is_binary(Topic) ->
|
||||||
triples(words(Topic), root, []).
|
triples(words(Topic), root, []).
|
||||||
|
|
||||||
|
@ -154,7 +156,7 @@ bin(B) when is_binary(B) -> B.
|
||||||
%% @doc Split Topic Path to Words
|
%% @doc Split Topic Path to Words
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec words(binary()) -> words().
|
-spec words(topic()) -> words().
|
||||||
words(Topic) when is_binary(Topic) ->
|
words(Topic) when is_binary(Topic) ->
|
||||||
[word(W) || W <- binary:split(Topic, <<"/">>, [global])].
|
[word(W) || W <- binary:split(Topic, <<"/">>, [global])].
|
||||||
|
|
||||||
|
@ -167,7 +169,7 @@ word(Bin) -> Bin.
|
||||||
%% @doc Queue is a special topic name that starts with "$Q/"
|
%% @doc Queue is a special topic name that starts with "$Q/"
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec is_queue(binary()) -> boolean().
|
-spec is_queue(topic()) -> boolean().
|
||||||
is_queue(<<"$Q/", _Queue/binary>>) ->
|
is_queue(<<"$Q/", _Queue/binary>>) ->
|
||||||
true;
|
true;
|
||||||
is_queue(_) ->
|
is_queue(_) ->
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%%%-----------------------------------------------------------------------------
|
%%%-----------------------------------------------------------------------------
|
||||||
%%% Copyright (c) 2012-2015 eMQTT.IO, All Rights Reserved.
|
%%% Copyright (c) 2012-2016 eMQTT.IO, All Rights Reserved.
|
||||||
%%%
|
%%%
|
||||||
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
%%% of this software and associated documentation files (the "Software"), to deal
|
%%% of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|