-
Notifications
You must be signed in to change notification settings - Fork 0
/
station.erl
208 lines (191 loc) · 7.87 KB
/
station.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
-module(station).
-export([start/5, start/6]).
-define(frameLength, 1000).
-define(slots, 25).
-define(slotlength, 40).
-define(no_slot, none).
%takes interface to use, target IP and Port, StationClass and initial TimeOffset as strings and starts the station
start(SInterface, SIP, SPort, SClass, SOffset) ->
start(SInterface, SIP, SPort, SClass, SOffset, 0).
start(SInterface, SIP, SPort, SClass, SOffset, TimeOffset) ->
%USE IN PRODUCTION!
Interface = getIfAddr(SInterface),
{ok, IP} = inet_parse:address(SIP),
{Port, _} = string:to_integer(SPort),
%USE WITH TESTER:
%Interface = SInterface,
%IP = SIP,
%Port = SPort,
Class = SClass,
{Offset, _} = string:to_integer(SOffset),
werkzeug:logging(lists:concat(["station",Offset,".log"]), "---starting Station---\r\n"),
TimeManager = spawn(timemanager, start, [TimeOffset, Offset]),
SlotManager = spawn(slotmanager, start, [?slots, Offset]),
Receiver = spawn(receiver, start, [Interface, Port, IP, TimeManager, Offset]),
%ONLY FOR TESTING:
spawn(tester, start, [Receiver, 10]),
Reader = spawn(reader, start, [Offset]),
Sender = spawn(sender, start, [Interface, IP, Port, Class, Reader, TimeManager, SlotManager, Offset]),
Senke = spawn(senke, start, [Offset]),
werkzeug:logging(lists:concat(["station",Offset,".log"]), "started all modules\r\n"),
ReservedSlot = {next, ?no_slot},
LastOwnSlot = -1,
LocalTime = getTime(TimeManager),
TimeNextSlot = ?slotlength - (LocalTime rem ?slotlength),
timer:send_after(TimeNextSlot, next_slot),
werkzeug:logging(lists:concat(["station",Offset,".log"]), lists:concat([LocalTime, ": started frame timer: ", TimeNextSlot, "\r\n"])),
communication(LastOwnSlot, ReservedSlot, Class, Receiver, Sender, TimeManager, SlotManager, Senke, Offset).
%lastOwnSlot: slot in dem zuletzt gesendet wurde
%reserved slot: {dieser oder nächster frame/reservierter slot}
communication(LastOwnSlot, ReservedSlot, StationClass, Receiver, Sender, TimeManager, SlotManager, Senke, SNo) ->
werkzeug:logging(lists:concat(["station",SNo,".log"]), "wait for next_slot \r\n"),
receive
next_slot ->
%get current time
LocalTime = getTime(TimeManager),
%compute current slot number
%reduce localtime to framelength then compute slotnumber (+1 because its based on 1 and not on 0).
CurFrameTime = (LocalTime rem ?frameLength),
CurrentSlot = (CurFrameTime div ?slotlength) + 1,
werkzeug:logging(lists:concat(["station",SNo,".log"]), lists:concat(["Station: ", LocalTime, ": ", CurFrameTime, " -> ", CurrentSlot, " ", ?slotlength, "\r\n"])),
%get message -> returns original slot if there is no collision with own message
%and delegates content, slot and timestamp information to respective modules
ReservedSlotAfterReceive = getMessage(ReservedSlot, LastOwnSlot, CurrentSlot, Receiver, TimeManager, SlotManager, Senke, SNo),
if
%if its first slot in frame:
CurrentSlot == 1 -> %start of new frame
%if there is no reservation for this frame, restart: pick a slot for this frame
werkzeug:logging(lists:concat(["station",SNo,".log"]), "=== new frame ===\r\n"),
{Frame, CurrentReservation} = ReservedSlotAfterReceive,
if
%no slot reserved last frame -> reserve one for this to enter
{Frame, CurrentReservation} == {this, ?no_slot} ->
EntrySlot = getNewSlot(SlotManager),
ReservedSlotAfterFrameStart = {this, EntrySlot},
werkzeug:logging(lists:concat(["station",SNo,".log"]), lists:concat(["Station: new entry with slot: ", EntrySlot, "\r\n"]));
%a slot reserved for next frame -> is a reservation for this frame, as a new one just started
Frame == next ->
ReservedSlotAfterFrameStart = {this, CurrentReservation};
true ->
io:format("received this-reservation at frame start ~p~n",[ReservedSlotAfterReceive]),
ReservedSlotAfterFrameStart = {this, CurrentReservation}
end,
%also reset slotmanager and timemanager for new frame
SlotManager ! {new_frame},
TimeManager ! {sync_time};
true ->
ReservedSlotAfterFrameStart = ReservedSlotAfterReceive
end,
{_, SomeReservation} = ReservedSlotAfterFrameStart,
werkzeug:logging(lists:concat(["station",SNo,".log"]), lists:concat(["Station: check for sending, Current: ", CurrentSlot, " Reserved: ", SomeReservation,", CurrentTime ", LocalTime, "\r\n"])),
%then check if currentSlot is own slot
if
{this, CurrentSlot} == ReservedSlotAfterFrameStart ->
%get content
SlotStart = (LocalTime - LocalTime rem ?slotlength),
SlotEnd = SlotStart + ?slotlength,
Sender ! {send, self(), SlotStart, SlotEnd},
receive
{ok, NewSlotNumber} ->
werkzeug:logging(lists:concat(["station",SNo,".log"]), lists:concat(["Station: Sent message, next Reservation: ", NewSlotNumber, "\r\n"]));
{missed} ->
NewSlotNumber = ?no_slot,
werkzeug:logging(lists:concat(["station",SNo,".log"]), lists:concat(["Station: missed slot: ", SlotStart, " - ", SlotEnd, "!\r\n"]))
end,
LastSlot = CurrentSlot,
NewSlotReservation = {next, NewSlotNumber};
true ->
LastSlot = LastOwnSlot,
NewSlotReservation = ReservedSlotAfterFrameStart
end,
CurTime = getTime(TimeManager),
TimeToNextSlot = ?slotlength - (CurTime rem ?slotlength),
timer:send_after(TimeToNextSlot, next_slot),
werkzeug:logging(lists:concat(["station",SNo,".log"]), lists:concat(["Station: next slot in time ", CurTime, " wait ", TimeToNextSlot, "\r\n"])),
communication(LastSlot, NewSlotReservation, StationClass, Receiver, Sender, TimeManager, SlotManager, Senke, SNo)
end.
%a new slot begins:
%compute currentSlot
%ask for messages of last slot
%-> if collision -> is it own collision? -> remove reservation
%else -> add slotreservation
% add timestamp if Station A
% add content
%
%frame start?
%-> yes
% no slot reserved -> reserve one
% sm ! reset
% tm ! reset
%
%get time
%current slot = reserved one?
%yes ->
% get message
% get slot
% get timestamp
% send message
%no ->
% reserved slot already missed?
% yes -> remove reservation
%
%compute time to next slot
%start timer to next slot
%Slot slot is needed to check for collisions of own messages
getMessage(ReservedSlot, LastOwnSlot, CurrentSlot, Receiver, TimeManager, SlotManager, Senke, SNo) ->
Receiver ! {get_message, self()},
receive
{msg, none} ->
werkzeug:logging(lists:concat(["station",SNo,".log"]), "got no message last Slot\r\n"),
ReservedSlot;
{msg, {"A", Content, Slot, TimestampSender,TimestampReceived}} ->
werkzeug:logging(lists:concat(["station",SNo,".log"]), "MESSAGE A last Slot\r\n"),
TimeManager ! {remote_time, TimestampSender, TimestampReceived},
extractData(Content, Slot, SlotManager, Senke),
ReservedSlot;
{msg, {"B", Content, Slot, _TimestampSender, _TimestampReceived}} ->
werkzeug:logging(lists:concat(["station",SNo,".log"]), "MESSAGE B last Slot\r\n"),
extractData(Content, Slot, SlotManager, Senke),
ReservedSlot;
{collision} ->
werkzeug:logging(lists:concat(["station",SNo,".log"]), "--COLLISION-- last Slot\r\n"),
if
CurrentSlot == LastOwnSlot ->
{this, ?no_slot};
true ->
ReservedSlot
end;
Any ->
io:format("unexpected message format: ~p~n",[Any])
end.
%helper functions
getIfAddr(Interface) ->
{ok, InterfaceList} = inet:getifaddrs(),
getAddr(Interface, InterfaceList).
getAddr(_Interface, []) ->
{addr, err};
getAddr(Interface, [{Interface, InterfaceOptions} | _Rest]) ->
getIP(InterfaceOptions);
getAddr(Interface, [_| Rest]) ->
getAddr(Interface, Rest).
getIP([]) ->
{addr, err};
getIP([{addr, Wert} | _Rest]) ->
Wert;
getIP([_| Rest]) ->
getIP(Rest).
extractData(Content, Slot, SlotManager, Senke) ->
SlotManager ! {reserve, Slot},
Senke ! {content, Content}.
getNewSlot(SlotManager) ->
SlotManager ! {get_slot, self()},
receive
{slot, Slot} ->
Slot
end.
getTime(TimeManager) ->
TimeManager ! {get_time, self()},
receive
{time, LocalTime} ->
LocalTime
end.