Skip to content

Commit

Permalink
Update channel setting (#31)
Browse files Browse the repository at this point in the history
This resolves #7.

I implemented the features as below.
- Receive a request to start/stop measurement of a specific channel
through `operation/:channel/`.
- Receive a request to update exposure time or period of a specific
channel through `setting/:channel/`.

The detailed logic for WLM control can be found in #7.

To test these features, you can mock the `WLM` class. The following code
might be helpful.
```python
class WLM:
    def open(self):
        print('WLM is connected.')

    def close(self):
        print('WLM is disconnected.')

    def start_measurement(self):
        print('Measurement is stared.')

    def stop_measurement(self):
        print('Measurement is stopped')

    def set_exposure(self, exposure, channel):
        print(f'Exposure time of channel {channel} is set to {exposure}.')
```

And the example client code is as follows.
```python
import requests

BASE_URL = 'http://localhost:8000'

with requests.Session() as session:
    data = {'username': 'user1', 'password': 'OOOO'}
    response = session.post(BASE_URL + '/user/signin/', json=data)

    csrf_token = session.cookies.get('csrftoken')
    headers = {'X-CSRFToken': csrf_token}

    data = {'exposure': 1}
    response = session.post(BASE_URL + '/setting/1/', json=data, headers=headers)
    
    data = {'period': 1}
    response = session.post(BASE_URL + '/setting/1/', json=data, headers=headers)

    data = {'on': True}
    response = session.post(BASE_URL + '/operation/1/', json=data, headers=headers)
    # open WLM connection and start measurement of channel 1

    data = {'on': True}
    response = session.post(BASE_URL + '/operation/2/', json=data, headers=headers)
    # start measurement of channel 2

    data = {'on': False}
    response = session.post(BASE_URL + '/operation/1/', json=data, headers=headers)
    # stop measurement of channel 1

    data = {'on': False}
    response = session.post(BASE_URL + '/operation/2/', json=data, headers=headers)
    # stop measurement of channel 2 and close WLM connection

    response = session.post(BASE_URL + '/user/signout/', headers=headers)
    print(response.status_code)  # 200
```

P.S. I updated `.pylintrc` file to allow more similar codes like
`iquip`.
  • Loading branch information
BECATRUE authored Nov 22, 2024
2 parents bff7938 + da8e55f commit 4e3dc0a
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 1 deletion.
3 changes: 3 additions & 0 deletions wlm_server/.pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ load-plugins=pylint_django
django-settings-module=wlm_server.settings
ignore=migrations
disable=missing-module-docstring, missing-class-docstring, missing-function-docstring, too-many-ancestors

[SIMILARITIES]
min-similarity-lines=10
7 changes: 7 additions & 0 deletions wlm_server/operation/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path

from . import views

urlpatterns = [
path('<int:ch>/', views.handle_info, name='handle channel operation'),
]
60 changes: 60 additions & 0 deletions wlm_server/operation/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.conf import settings
from rest_framework.decorators import api_view

from operation.models import Operation
from channel.models import Channel
from task.message import ActionType, MessageInfo, MessageQueue
from task.handler import TaskHandler

def get_running_status(ch: int) -> tuple[bool, bool]:
"""Gets running status of WLM and the given channel.
Args:
ch: Target channel.
Returns:
Tuple with WLM running status and target channel running
"""
latest_operations = (
Operation.objects.order_by('channel', 'user', '-occurred_at').distinct('channel', 'user')
) # latest operations for each channel and user
is_wlm_running = any(op.on for op in latest_operations)
is_channel_running = any(op.channel.channel == ch and op.on for op in latest_operations)
return is_wlm_running, is_channel_running


@login_required
@api_view(['POST'])
def handle_info(request, ch: int):
user = request.user
try:
channel = Channel.objects.get(channel=ch)
except Channel.DoesNotExist:
return HttpResponse(status=404)
if not channel.teams.contains(user.team):
return HttpResponse(status=403)
message_queue: MessageQueue = settings.MESSAGE_QUEUE
req_data = request.data.copy()
on = req_data['on']
operation = Operation(user=user, channel=channel, on=on)
if on:
is_wlm_running, is_channel_running = get_running_status(ch)
if not is_wlm_running:
task_handler = TaskHandler()
task_handler.start()
if not is_channel_running:
message = MessageInfo(ActionType.OPERATE, ch, {'on': True})
message_queue.push(message)
operation.save()
else:
operation.save()
is_wlm_running, is_channel_running = get_running_status(ch)
if not is_channel_running:
message = MessageInfo(ActionType.OPERATE, ch, {'on': False})
message_queue.push(message)
if not is_wlm_running:
message = MessageInfo(ActionType.CLOSE, None, None)
message_queue.push(message)
return HttpResponse(status=200)
7 changes: 7 additions & 0 deletions wlm_server/setting/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path

from . import views

urlpatterns = [
path('<int:ch>/', views.handle_info, name='handle channel setting'),
]
41 changes: 41 additions & 0 deletions wlm_server/setting/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from datetime import timedelta

from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.conf import settings
from rest_framework.decorators import api_view

from setting.models import Setting
from channel.models import Channel
from task.message import ActionType, MessageInfo, MessageQueue

@login_required
@api_view(['POST'])
def handle_info(request, ch: int):
user = request.user
try:
channel = Channel.objects.get(channel=ch)
except Channel.DoesNotExist:
return HttpResponse(status=404)
if not channel.teams.contains(user.team):
return HttpResponse(status=403)
message_queue: MessageQueue = settings.MESSAGE_QUEUE
latest_setting = Setting.objects.filter(channel__channel=ch).order_by('-created_at').first()
if latest_setting is None:
exposure, period = None, None
else:
exposure, period = latest_setting.exposure, latest_setting.period
req_data = request.data.copy()
if 'exposure' in req_data:
exposure_s = req_data['exposure']
exposure = timedelta(seconds=exposure_s)
message = MessageInfo(ActionType.EXPOSURE, ch, {'exposure': exposure})
message_queue.push(message)
if 'period' in req_data:
period_s = req_data['period']
period = timedelta(seconds=period_s)
message = MessageInfo(ActionType.PERIOD, ch, {'period': period})
message_queue.push(message)
setting = Setting(channel=channel, exposure=exposure, period=period)
setting.save()
return HttpResponse(status=200)
3 changes: 2 additions & 1 deletion wlm_server/task/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class TaskHandler(threading.Thread):

def __init__(self):
super().__init__()
self.daemon = True
self._wlm: WLM
self._message_queue: MessageQueue = settings.MESSAGE_QUEUE
self._measure_queue: MeasureQueue = MeasureQueue()
Expand All @@ -41,7 +42,7 @@ def _close_connection(self):
self._wlm.close()

def _start_channel_measurement(self, channel: int):
setting = Setting.objects.filter(channel__name=channel).order_by('-created_at').first()
setting = Setting.objects.filter(channel__channel=channel).order_by('-created_at').first()
period = setting.period
self._channel_to_period[channel] = period
measure = MeasureInfo(channel, datetime.now())
Expand Down
2 changes: 2 additions & 0 deletions wlm_server/wlm_server/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@
urlpatterns = [
path('user/', include('user.urls')),
path('channel/', include('channel.urls')),
path('setting/', include('setting.urls')),
path('operation/', include('operation.urls')),
path('admin/', admin.site.urls),
]

0 comments on commit 4e3dc0a

Please sign in to comment.