{"action":"created","comment":{"url":"https://api.github.com/repos/BrainTech/openbci/pulls/comments/59721529","id":59721529,"diff_hunk":"@@ -0,0 +1,222 @@\n+#!/usr/bin/env python\n+# -*- coding: utf-8 -*-\n+\n+import abc, os, pickle\n+from threading import Thread\n+from Queue import Queue\n+\n+from multiplexer.multiplexer_constants import peers, types\n+from obci.control.peer.configured_multiplexer_server import ConfiguredMultiplexerServer\n+from obci.configs import variables_pb2\n+from obci.utils.openbci_logging import log_crash\n+\n+\n+class AnalysisMaster(ConfiguredMultiplexerServer):\n+    \"\"\"Base class for all peers implementing BCI\n+    (can be used for both calibration and decision phase).\"\"\"\n+    __metaclass__ = abc.ABCMeta\n+\n+    # ----------------\n+    # abstract methods\n+    # ----------------\n+\n+    @abc.abstractmethod\n+    def add_result(self, blink, probabilities):\n+        \"\"\"This method is called whenever a classification result, consisting of\n+        a single blink and classification probabilities, is available.\n+        The implementation may simply send decision message based on the highest\n+        probability, or perform some advanced result post-processing.\n+\n+        Needs to be implemented in subclass.\n+\n+        Args:\n+            blink: information contained in a single blink\n+            probabilities (dict): dictionary of {target: probability}\n+        \"\"\"\n+        pass\n+\n+    @abc.abstractmethod\n+    def create_buffer(self, channel_count, ret_func):\n+        \"\"\"Create an empty buffer with given return function.\n+        Needs to be implemented in subclass.\n+\n+        Args:\n+            channel_count (int): number of channels in the input signal\n+            ret_func: function which should be passed to a buffer as \"ret_func\"\n+\n+        Returns:\n+            buffer instance\n+        \"\"\"\n+        return None\n+\n+    @abc.abstractmethod\n+    def create_classifier(self):\n+        \"\"\"Create a classifier to be used.\n+        Needs to be implemented in subclass.\n+\n+        Returns:\n+            AbstractClassifier. classifier instance\n+        \"\"\"\n+        return None\n+\n+    @abc.abstractmethod\n+    def identify_blink(self, blink):\n+        \"\"\"Identify to which target the given blink belongs, based on\n+        some external (e.g. GUI) information. If this information\n+        is not available (outside calibration phase), return None.\n+        This method is NOT meant to use machine learning techniques.\n+\n+        Needs to be implemented in subclass.\n+        This method will be run by a separate thread.\n+\n+        Args:\n+            blink: information contained in a single blink\n+\n+        Returns:\n+            string. name of the target\n+            or None if identification could not be performed\n+        \"\"\"\n+        return None\n+\n+    @abc.abstractmethod\n+    def init_params(self):\n+        \"\"\"Needs to be implemented in subclass.\"\"\"\n+        pass\n+\n+    # -------------------------------\n+    # optionally overridable methods\n+    # -------------------------------\n+\n+    def classify(self, classifier, chunk, blink):\n+        \"\"\"Compute set of classification probabilities for a given blink.\n+\n+        May be re-implemented in subclass to perform some pre-processing\n+        or feature extraction on the signal (chunk).\n+        This method will be run by a separate thread.\n+\n+        Args:\n+            classifier (AbstractClassifier): classifier instance to be used for classification\n+            chunk: numpy 2D data array (channels × samples)\n+            blink: information contained in a single blink\n+\n+        Returns:\n+            dict. dictionary of {target: probability},\n+            or None if classification could not be performed\n+        \"\"\"\n+        return classifier.classify(chunk)\n+\n+    def learn(self, classifier, chunk, target):\n+        \"\"\"Learn that given chunk represents given target.\n+\n+        May be re-implemented in subclass to perform some pre-processing\n+        or feature extraction on the signal (chunk).\n+        This method will be run by a separate thread.\n+\n+        Args:\n+            classifier (AbstractClassifier): classifier instance to be used for learning\n+            chunk: numpy 2D data array (channels × samples)\n+            target: name of the target\n+        \"\"\"\n+        classifier.learn(chunk, target)\n+\n+    # ----------------\n+    # internal methods\n+    # ----------------\n+\n+    @log_crash\n+    def __init__(self, addresses):\n+        super(AnalysisMaster, self).__init__(addresses=addresses, type=peers.ANALYSIS)\n+        # initialize parameters of the subclass\n+        self.init_params()\n+        channel_count = len(self.config.get_param('channel_names').split(';'))\n+        self.buffer = self.create_buffer(channel_count, self._buffer_ret_func)\n+\n+        # load classifier from file, if requested\n+        self.wisdom_path = self.config.get_param('wisdom_path') if self.config.has_param('wisdom_path') else None\n+        self._classifier = self._get_classifier()\n+\n+        # array of requests to classification thread\n+        self.queue_classification = Queue()\n+        # array of requests to learning thread\n+        self.queue_learning = Queue()\n+\n+        # separate thread for classification\n+        classification = Thread(target=self._run_classification)\n+        classification.daemon = True\n+        classification.start()\n+\n+        # separate thread for learning\n+        learning = Thread(target=self._run_learning)\n+        learning.daemon = True\n+        learning.start()\n+\n+        self.ready()\n+\n+    def _buffer_ret_func(self, blink, chunk=None):\n+        \"\"\"Internal function for the buffer object.\n+\n+        Args:\n+            blink: information contained in a single blink\n+            chunk: numpy 2D data array (channels × samples)\n+        \"\"\"\n+        if chunk is None:\n+            chunk = blink\n+            blink = None\n+        request = {'chunk': chunk, 'blink': blink}\n+        target = self.identify_blink(blink)\n+        if target is None:\n+            self.queue_classification.put(request)\n+        else:\n+            request['target'] = target\n+            self.queue_learning.put(request)\n+\n+    def _get_classifier(self):\n+        \"\"\"Provide a classifier object from file or create a new one.\n+\n+        Returns:\n+            AbstractClassifier. a classifier object\n+        \"\"\"\n+        if self.config.has_param('wisdom_load') and os.path.isfile(self.wisdom_path):\n+            return pickle.load(self.wisdom_path)\n+        else:\n+            return self.create_classifier()\n+\n+    def _run_classification(self):\n+        \"\"\"This method will be run as a separate thread.\"\"\"\n+        while True:\n+            request = self.queue_classification.get()\n+            probabilities = self.classify(self._classifier, request['chunk'], request['blink'])\n+            if probabilities is not None:\n+                self.add_result(request['blink'], probabilities)\n+\n+    def _run_learning(self):\n+        \"\"\"This method will be run as a separate thread.\"\"\"\n+        while True:\n+            request = self.queue_learning.get()\n+            clone = self._classifier.clone()","path":"obci/interfaces/bci/analysis_master.py","position":196,"original_position":196,"commit_id":"30fc63370bc28fee2a40098aec3e010f3e61595b","original_commit_id":"692bd950c75902e837da9314201ecc34170e1d9c","user":{"login":"develancer","id":10493115,"avatar_url":"https://avatars.githubusercontent.com/u/10493115?v=3","gravatar_id":"","url":"https://api.github.com/users/develancer","html_url":"https://github.com/develancer","followers_url":"https://api.github.com/users/develancer/followers","following_url":"https://api.github.com/users/develancer/following{/other_user}","gists_url":"https://api.github.com/users/develancer/gists{/gist_id}","starred_url":"https://api.github.com/users/develancer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/develancer/subscriptions","organizations_url":"https://api.github.com/users/develancer/orgs","repos_url":"https://api.github.com/users/develancer/repos","events_url":"https://api.github.com/users/develancer/events{/privacy}","received_events_url":"https://api.github.com/users/develancer/received_events","type":"User","site_admin":false},"body":"Yes, and the logic you're writing about can be implemented by overriding AnalysisMaster's _learn_ method.","created_at":"2016-04-14T13:59:48Z","updated_at":"2016-04-14T13:59:48Z","html_url":"https://github.com/BrainTech/openbci/pull/5#discussion_r59721529","pull_request_url":"https://api.github.com/repos/BrainTech/openbci/pulls/5","_links":{"self":{"href":"https://api.github.com/repos/BrainTech/openbci/pulls/comments/59721529"},"html":{"href":"https://github.com/BrainTech/openbci/pull/5#discussion_r59721529"},"pull_request":{"href":"https://api.github.com/repos/BrainTech/openbci/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/BrainTech/openbci/pulls/5","id":66151772,"html_url":"https://github.com/BrainTech/openbci/pull/5","diff_url":"https://github.com/BrainTech/openbci/pull/5.diff","patch_url":"https://github.com/BrainTech/openbci/pull/5.patch","issue_url":"https://api.github.com/repos/BrainTech/openbci/issues/5","number":5,"state":"open","locked":false,"title":"nowy abstrakcyjny peer i klasyfikator","user":{"login":"develancer","id":10493115,"avatar_url":"https://avatars.githubusercontent.com/u/10493115?v=3","gravatar_id":"","url":"https://api.github.com/users/develancer","html_url":"https://github.com/develancer","followers_url":"https://api.github.com/users/develancer/followers","following_url":"https://api.github.com/users/develancer/following{/other_user}","gists_url":"https://api.github.com/users/develancer/gists{/gist_id}","starred_url":"https://api.github.com/users/develancer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/develancer/subscriptions","organizations_url":"https://api.github.com/users/develancer/orgs","repos_url":"https://api.github.com/users/develancer/repos","events_url":"https://api.github.com/users/develancer/events{/privacy}","received_events_url":"https://api.github.com/users/develancer/received_events","type":"User","site_admin":false},"body":"@mroja @achabuda @mdovgialo ","created_at":"2016-04-12T12:52:32Z","updated_at":"2016-04-14T13:59:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"af0dc7daa0e2d51b7b07faa46f97d636d6ed234c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/BrainTech/openbci/pulls/5/commits","review_comments_url":"https://api.github.com/repos/BrainTech/openbci/pulls/5/comments","review_comment_url":"https://api.github.com/repos/BrainTech/openbci/pulls/comments{/number}","comments_url":"https://api.github.com/repos/BrainTech/openbci/issues/5/comments","statuses_url":"https://api.github.com/repos/BrainTech/openbci/statuses/30fc63370bc28fee2a40098aec3e010f3e61595b","head":{"label":"BrainTech:ptr-generic-classifier","ref":"ptr-generic-classifier","sha":"30fc63370bc28fee2a40098aec3e010f3e61595b","user":{"login":"BrainTech","id":8409926,"avatar_url":"https://avatars.githubusercontent.com/u/8409926?v=3","gravatar_id":"","url":"https://api.github.com/users/BrainTech","html_url":"https://github.com/BrainTech","followers_url":"https://api.github.com/users/BrainTech/followers","following_url":"https://api.github.com/users/BrainTech/following{/other_user}","gists_url":"https://api.github.com/users/BrainTech/gists{/gist_id}","starred_url":"https://api.github.com/users/BrainTech/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BrainTech/subscriptions","organizations_url":"https://api.github.com/users/BrainTech/orgs","repos_url":"https://api.github.com/users/BrainTech/repos","events_url":"https://api.github.com/users/BrainTech/events{/privacy}","received_events_url":"https://api.github.com/users/BrainTech/received_events","type":"Organization","site_admin":false},"repo":{"id":22815770,"name":"openbci","full_name":"BrainTech/openbci","owner":{"login":"BrainTech","id":8409926,"avatar_url":"https://avatars.githubusercontent.com/u/8409926?v=3","gravatar_id":"","url":"https://api.github.com/users/BrainTech","html_url":"https://github.com/BrainTech","followers_url":"https://api.github.com/users/BrainTech/followers","following_url":"https://api.github.com/users/BrainTech/following{/other_user}","gists_url":"https://api.github.com/users/BrainTech/gists{/gist_id}","starred_url":"https://api.github.com/users/BrainTech/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BrainTech/subscriptions","organizations_url":"https://api.github.com/users/BrainTech/orgs","repos_url":"https://api.github.com/users/BrainTech/repos","events_url":"https://api.github.com/users/BrainTech/events{/privacy}","received_events_url":"https://api.github.com/users/BrainTech/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/BrainTech/openbci","description":"OpenBCI Framework","fork":false,"url":"https://api.github.com/repos/BrainTech/openbci","forks_url":"https://api.github.com/repos/BrainTech/openbci/forks","keys_url":"https://api.github.com/repos/BrainTech/openbci/keys{/key_id}","collaborators_url":"https://api.github.com/repos/BrainTech/openbci/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/BrainTech/openbci/teams","hooks_url":"https://api.github.com/repos/BrainTech/openbci/hooks","issue_events_url":"https://api.github.com/repos/BrainTech/openbci/issues/events{/number}","events_url":"https://api.github.com/repos/BrainTech/openbci/events","assignees_url":"https://api.github.com/repos/BrainTech/openbci/assignees{/user}","branches_url":"https://api.github.com/repos/BrainTech/openbci/branches{/branch}","tags_url":"https://api.github.com/repos/BrainTech/openbci/tags","blobs_url":"https://api.github.com/repos/BrainTech/openbci/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/BrainTech/openbci/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/BrainTech/openbci/git/refs{/sha}","trees_url":"https://api.github.com/repos/BrainTech/openbci/git/trees{/sha}","statuses_url":"https://api.github.com/repos/BrainTech/openbci/statuses/{sha}","languages_url":"https://api.github.com/repos/BrainTech/openbci/languages","stargazers_url":"https://api.github.com/repos/BrainTech/openbci/stargazers","contributors_url":"https://api.github.com/repos/BrainTech/openbci/contributors","subscribers_url":"https://api.github.com/repos/BrainTech/openbci/subscribers","subscription_url":"https://api.github.com/repos/BrainTech/openbci/subscription","commits_url":"https://api.github.com/repos/BrainTech/openbci/commits{/sha}","git_commits_url":"https://api.github.com/repos/BrainTech/openbci/git/commits{/sha}","comments_url":"https://api.github.com/repos/BrainTech/openbci/comments{/number}","issue_comment_url":"https://api.github.com/repos/BrainTech/openbci/issues/comments{/number}","contents_url":"https://api.github.com/repos/BrainTech/openbci/contents/{+path}","compare_url":"https://api.github.com/repos/BrainTech/openbci/compare/{base}...{head}","merges_url":"https://api.github.com/repos/BrainTech/openbci/merges","archive_url":"https://api.github.com/repos/BrainTech/openbci/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/BrainTech/openbci/downloads","issues_url":"https://api.github.com/repos/BrainTech/openbci/issues{/number}","pulls_url":"https://api.github.com/repos/BrainTech/openbci/pulls{/number}","milestones_url":"https://api.github.com/repos/BrainTech/openbci/milestones{/number}","notifications_url":"https://api.github.com/repos/BrainTech/openbci/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/BrainTech/openbci/labels{/name}","releases_url":"https://api.github.com/repos/BrainTech/openbci/releases{/id}","deployments_url":"https://api.github.com/repos/BrainTech/openbci/deployments","created_at":"2014-08-10T18:34:59Z","updated_at":"2016-04-07T17:10:08Z","pushed_at":"2016-04-14T13:55:10Z","git_url":"git://github.com/BrainTech/openbci.git","ssh_url":"git@github.com:BrainTech/openbci.git","clone_url":"https://github.com/BrainTech/openbci.git","svn_url":"https://github.com/BrainTech/openbci","homepage":null,"size":62932,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":4,"default_branch":"master"}},"base":{"label":"BrainTech:master","ref":"master","sha":"3192279e9c18fd4b346cbd65197b090d1d11c117","user":{"login":"BrainTech","id":8409926,"avatar_url":"https://avatars.githubusercontent.com/u/8409926?v=3","gravatar_id":"","url":"https://api.github.com/users/BrainTech","html_url":"https://github.com/BrainTech","followers_url":"https://api.github.com/users/BrainTech/followers","following_url":"https://api.github.com/users/BrainTech/following{/other_user}","gists_url":"https://api.github.com/users/BrainTech/gists{/gist_id}","starred_url":"https://api.github.com/users/BrainTech/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BrainTech/subscriptions","organizations_url":"https://api.github.com/users/BrainTech/orgs","repos_url":"https://api.github.com/users/BrainTech/repos","events_url":"https://api.github.com/users/BrainTech/events{/privacy}","received_events_url":"https://api.github.com/users/BrainTech/received_events","type":"Organization","site_admin":false},"repo":{"id":22815770,"name":"openbci","full_name":"BrainTech/openbci","owner":{"login":"BrainTech","id":8409926,"avatar_url":"https://avatars.githubusercontent.com/u/8409926?v=3","gravatar_id":"","url":"https://api.github.com/users/BrainTech","html_url":"https://github.com/BrainTech","followers_url":"https://api.github.com/users/BrainTech/followers","following_url":"https://api.github.com/users/BrainTech/following{/other_user}","gists_url":"https://api.github.com/users/BrainTech/gists{/gist_id}","starred_url":"https://api.github.com/users/BrainTech/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BrainTech/subscriptions","organizations_url":"https://api.github.com/users/BrainTech/orgs","repos_url":"https://api.github.com/users/BrainTech/repos","events_url":"https://api.github.com/users/BrainTech/events{/privacy}","received_events_url":"https://api.github.com/users/BrainTech/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/BrainTech/openbci","description":"OpenBCI Framework","fork":false,"url":"https://api.github.com/repos/BrainTech/openbci","forks_url":"https://api.github.com/repos/BrainTech/openbci/forks","keys_url":"https://api.github.com/repos/BrainTech/openbci/keys{/key_id}","collaborators_url":"https://api.github.com/repos/BrainTech/openbci/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/BrainTech/openbci/teams","hooks_url":"https://api.github.com/repos/BrainTech/openbci/hooks","issue_events_url":"https://api.github.com/repos/BrainTech/openbci/issues/events{/number}","events_url":"https://api.github.com/repos/BrainTech/openbci/events","assignees_url":"https://api.github.com/repos/BrainTech/openbci/assignees{/user}","branches_url":"https://api.github.com/repos/BrainTech/openbci/branches{/branch}","tags_url":"https://api.github.com/repos/BrainTech/openbci/tags","blobs_url":"https://api.github.com/repos/BrainTech/openbci/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/BrainTech/openbci/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/BrainTech/openbci/git/refs{/sha}","trees_url":"https://api.github.com/repos/BrainTech/openbci/git/trees{/sha}","statuses_url":"https://api.github.com/repos/BrainTech/openbci/statuses/{sha}","languages_url":"https://api.github.com/repos/BrainTech/openbci/languages","stargazers_url":"https://api.github.com/repos/BrainTech/openbci/stargazers","contributors_url":"https://api.github.com/repos/BrainTech/openbci/contributors","subscribers_url":"https://api.github.com/repos/BrainTech/openbci/subscribers","subscription_url":"https://api.github.com/repos/BrainTech/openbci/subscription","commits_url":"https://api.github.com/repos/BrainTech/openbci/commits{/sha}","git_commits_url":"https://api.github.com/repos/BrainTech/openbci/git/commits{/sha}","comments_url":"https://api.github.com/repos/BrainTech/openbci/comments{/number}","issue_comment_url":"https://api.github.com/repos/BrainTech/openbci/issues/comments{/number}","contents_url":"https://api.github.com/repos/BrainTech/openbci/contents/{+path}","compare_url":"https://api.github.com/repos/BrainTech/openbci/compare/{base}...{head}","merges_url":"https://api.github.com/repos/BrainTech/openbci/merges","archive_url":"https://api.github.com/repos/BrainTech/openbci/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/BrainTech/openbci/downloads","issues_url":"https://api.github.com/repos/BrainTech/openbci/issues{/number}","pulls_url":"https://api.github.com/repos/BrainTech/openbci/pulls{/number}","milestones_url":"https://api.github.com/repos/BrainTech/openbci/milestones{/number}","notifications_url":"https://api.github.com/repos/BrainTech/openbci/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/BrainTech/openbci/labels{/name}","releases_url":"https://api.github.com/repos/BrainTech/openbci/releases{/id}","deployments_url":"https://api.github.com/repos/BrainTech/openbci/deployments","created_at":"2014-08-10T18:34:59Z","updated_at":"2016-04-07T17:10:08Z","pushed_at":"2016-04-14T13:55:10Z","git_url":"git://github.com/BrainTech/openbci.git","ssh_url":"git@github.com:BrainTech/openbci.git","clone_url":"https://github.com/BrainTech/openbci.git","svn_url":"https://github.com/BrainTech/openbci","homepage":null,"size":62932,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/BrainTech/openbci/pulls/5"},"html":{"href":"https://github.com/BrainTech/openbci/pull/5"},"issue":{"href":"https://api.github.com/repos/BrainTech/openbci/issues/5"},"comments":{"href":"https://api.github.com/repos/BrainTech/openbci/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/BrainTech/openbci/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/BrainTech/openbci/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/BrainTech/openbci/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/BrainTech/openbci/statuses/30fc63370bc28fee2a40098aec3e010f3e61595b"}}}}