<commit_before>% camera-control – Control Sanyo PTZ cameras with a custom input board
%
% Copyright © 2009 <NAME>
%
% Permission to use, copy, modify, and/or distribute this software for any
% purpose with or without fee is hereby granted, provided that the above
% copyright notice and this permission notice appear in all copies.
%
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module (database).

-define (REGNAME, ?MODULE).

-behaviour (gen_server).

%% API
-export ([start_link/0, dump/0, action_lookup/2, action_lookup/1,
          action_is_special/1, action_set/3, ssp_camera_init/1,
          ssp_camera_slot_free/2, ssp_camera_slot_is_allocated/2,
          ssp_camera_slot_allocate/1]).

%% gen_server callbacks
-export ([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,
          code_change/3]).

%% includes
-include ("database.hrl").
-include ("ssp_constants.hrl").

%% State
-record (state, {dir, actions, ssp_camera_slots}).


%% API

start_link () ->
  gen_server:start_link ({local, ?REGNAME}, ?MODULE, dummy, []).

dump () ->
  gen_server:call (?REGNAME, dump).

action_lookup (Id, Unref) ->
  gen_server:call (?REGNAME, {action_lookup, Id, Unref}).

action_lookup (Id) ->
  action_lookup (Id, true).

action_is_special (Id) ->
  gen_server:call (?REGNAME, {action_is_special, Id}).

action_set (Id, Action, Special) ->
  gen_server:call (?REGNAME, {action_set, Id, Action, Special}).

ssp_camera_init (Addr) ->
  gen_server:call (?REGNAME, {ssp_camera_init, Addr}).

ssp_camera_slot_free (Addr, Slot) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_free, Addr, Slot}).

ssp_camera_slot_is_allocated (Addr, Slot) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_is_allocated, Addr, Slot}).

ssp_camera_slot_allocate (Addr) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_allocate, Addr}).


%% gen_server callbacks

init (dummy) ->
  process_flag (trap_exit, true),

  {ok, Dir} = application:get_env (camera_control, database_dir),

  {ok, Actions} = read_table (actions, Dir),
  {ok, SspCameraSlots} = read_table (ssp_camera_slots, Dir),

  {ok, #state{dir=Dir, actions=Actions, ssp_camera_slots=SspCameraSlots}}.

terminate (_Reason, _State) ->
  ok.


handle_call (dump, _From, State) ->
  dump_table (State#state.actions),
  dump_table (State#state.ssp_camera_slots),
  {reply, ok, State};


handle_call ({action_lookup, Id, Unref}, _From, State) ->
  Table = State#state.actions,

  Result = case action_lookup_maybe_unref (Table, Id, Unref) of
    {ok, Entry} ->
      {ok, Entry#action.action};
    _ ->
      {error, not_found} end,

  {reply, Result, State};

handle_call ({action_is_special, Id}, _From, State) ->
  Table = State#state.actions,

  Result = case ets:lookup (Table, Id) of
    [Entry] when Entry#action.special ->
      true;
    _ ->
      false end,

  {reply, Result, State};

handle_call ({action_set, Id, Action, Special}, _From, State) ->
  error_logger:info_msg ("Action: set ~p: ~p", [Id, Action]),

  Table = State#state.actions,

  ets:insert (Table, #action{id=Id, action=Action, special=Special}),
  ok = write_table (Table, State#state.dir),

  {reply, ok, State};


handle_call ({ssp_camera_init, Addr}, _From, State) ->
  Table = State#state.ssp_camera_slots,

  case ets:match_object (Table,
                         #ssp_camera_slot{id='_', camera_addr=Addr,
                                          slot='_', allocated='_'},
                         1) of
    '$end_of_table' ->
      % No slots exist for the camera.  Create them.
      error_logger:info_msg ("SSP camera ~p: Initializing database", [Addr]),

      lists:foreach (fun (Slot) ->
          Id = ssp_camera_slot_id (Addr, Slot),
          ets:insert (Table, #ssp_camera_slot{id=Id, camera_addr=Addr,
                                              slot=Slot}) end,
        lists:seq (?SSP_PRESET_MIN, ?SSP_PRESET_MAX)),

      ok = write_table (Table, State#state.dir);

    _ ->
      void end,
  {reply, ok, State};

handle_call ({ssp_camera_slot_free, Addr, Slot}, _From, State) ->
  Table = State#state.ssp_camera_slots,
  Id = ssp_camera_slot_id (Addr, Slot),

  error_logger:info_msg ("SSP camera ~p: Freeing slot ~p", [Addr, Slot]),

  [Entry] = ets:lookup (Table, Id),
  ets:insert (Table, Entry#ssp_camera_slot{allocated=false}),
  ok = write_table (Table, State#state.dir),

  {reply, ok, State};

handle_call ({ssp_camera_slot_is_allocated, Addr, Slot}, _From, State) ->
  Table = State#state.ssp_camera_slots,
  Id = ssp_camera_slot_id (Addr, Slot),

  Result = case ets:lookup (Table, Id) of
    [Entry] when Entry#ssp_camera_slot.allocated ->
      true;
    [] ->
      false end,

  {reply, Result, State};

handle_call ({ssp_camera_slot_allocate, Addr}, _From, State) ->
  Table = State#state.ssp_camera_slots,

  Result = case ets:match_object (Table,
                                  #ssp_camera_slot{id='_', camera_addr=Addr,
                                                   slot='_', allocated=false},
                                  1) of
    {[Entry], _} ->
      error_logger:info_msg ("SSP camera ~p: Allocating slot ~p",
                             [Addr, Entry#ssp_camera_slot.slot]),

      ets:insert (Table, Entry#ssp_camera_slot{allocated=true}),
      ok = write_table (Table, State#state.dir),

      {ok, Entry#ssp_camera_slot.slot};

    '$end_of_table' ->
      {error, no_free_slots} end,

  {reply, Result, State}.


handle_cast (_Request, State) ->
  {noreply, State}.

handle_info (_Info, State) ->
  {noreply, State}.

code_change (_OldVsn, State, _Extra) ->
  {ok, State}.


%% private

read_table (Name, Dir) ->
  Filename = atom_to_list (Name),

  Path = filename:join (Dir, Filename++".table"),

  error_logger:info_msg ("Loading ~p", [Path]),

  {Exists, List} = case file:consult (Path) of
    {ok, [Data]} ->
      {true, Data};
    {error, enoent} ->
      {false, []} end,

  Table = ets:new (Name, [{keypos, 2}]),
  ets:insert (Table, List),
<commit_msg>database: Allow allocating predefined slot<commit_after>% camera-control – Control Sanyo PTZ cameras with a custom input board
%
% Copyright © 2009 <NAME>
%
% Permission to use, copy, modify, and/or distribute this software for any
% purpose with or without fee is hereby granted, provided that the above
% copyright notice and this permission notice appear in all copies.
%
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

-module (database).

-define (REGNAME, ?MODULE).

-behaviour (gen_server).

%% API
-export ([start_link/0, dump/0, action_lookup/2, action_lookup/1,
          action_is_special/1, action_set/3, ssp_camera_init/1,
          ssp_camera_slot_free/2, ssp_camera_slot_is_allocated/2,
          ssp_camera_slot_allocate/1, ssp_camera_slot_allocate/2]).

%% gen_server callbacks
-export ([init/1, terminate/2, handle_call/3, handle_cast/2, handle_info/2,
          code_change/3]).

%% includes
-include ("database.hrl").
-include ("ssp_constants.hrl").

%% State
-record (state, {dir, actions, ssp_camera_slots}).


%% API

start_link () ->
  gen_server:start_link ({local, ?REGNAME}, ?MODULE, dummy, []).

dump () ->
  gen_server:call (?REGNAME, dump).

action_lookup (Id, Unref) ->
  gen_server:call (?REGNAME, {action_lookup, Id, Unref}).

action_lookup (Id) ->
  action_lookup (Id, true).

action_is_special (Id) ->
  gen_server:call (?REGNAME, {action_is_special, Id}).

action_set (Id, Action, Special) ->
  gen_server:call (?REGNAME, {action_set, Id, Action, Special}).

ssp_camera_init (Addr) ->
  gen_server:call (?REGNAME, {ssp_camera_init, Addr}).

ssp_camera_slot_free (Addr, Slot) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_free, Addr, Slot}).

ssp_camera_slot_is_allocated (Addr, Slot) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_is_allocated, Addr, Slot}).

ssp_camera_slot_allocate (Addr) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_allocate, Addr}).

ssp_camera_slot_allocate (Addr, Slot) ->
  gen_server:call (?REGNAME, {ssp_camera_slot_allocate, Addr, Slot}).


%% gen_server callbacks

init (dummy) ->
  process_flag (trap_exit, true),

  {ok, Dir} = application:get_env (camera_control, database_dir),

  {ok, Actions} = read_table (actions, Dir),
  {ok, SspCameraSlots} = read_table (ssp_camera_slots, Dir),

  {ok, #state{dir=Dir, actions=Actions, ssp_camera_slots=SspCameraSlots}}.

terminate (_Reason, _State) ->
  ok.


handle_call (dump, _From, State) ->
  dump_table (State#state.actions),
  dump_table (State#state.ssp_camera_slots),
  {reply, ok, State};


handle_call ({action_lookup, Id, Unref}, _From, State) ->
  Table = State#state.actions,

  Result = case action_lookup_maybe_unref (Table, Id, Unref) of
    {ok, Entry} ->
      {ok, Entry#action.action};
    _ ->
      {error, not_found} end,

  {reply, Result, State};

handle_call ({action_is_special, Id}, _From, State) ->
  Table = State#state.actions,

  Result = case ets:lookup (Table, Id) of
    [Entry] when Entry#action.special ->
      true;
    _ ->
      false end,

  {reply, Result, State};

handle_call ({action_set, Id, Action, Special}, _From, State) ->
  error_logger:info_msg ("Action: set ~p: ~p", [Id, Action]),

  Table = State#state.actions,

  ets:insert (Table, #action{id=Id, action=Action, special=Special}),
  ok = write_table (Table, State#state.dir),

  {reply, ok, State};


handle_call ({ssp_camera_init, Addr}, _From, State) ->
  Table = State#state.ssp_camera_slots,

  case ets:match_object (Table,
                         #ssp_camera_slot{id='_', camera_addr=Addr,
                                          slot='_', allocated='_'},
                         1) of
    '$end_of_table' ->
      % No slots exist for the camera.  Create them.
      error_logger:info_msg ("SSP camera ~p: Initializing database", [Addr]),

      lists:foreach (fun (Slot) ->
          Id = ssp_camera_slot_id (Addr, Slot),
          ets:insert (Table, #ssp_camera_slot{id=Id, camera_addr=Addr,
                                              slot=Slot}) end,
        lists:seq (?SSP_PRESET_MIN, ?SSP_PRESET_MAX)),

      ok = write_table (Table, State#state.dir);

    _ ->
      void end,
  {reply, ok, State};

handle_call ({ssp_camera_slot_free, Addr, Slot}, _From, State) ->
  Table = State#state.ssp_camera_slots,
  Id = ssp_camera_slot_id (Addr, Slot),

  error_logger:info_msg ("SSP camera ~p: Freeing slot ~p", [Addr, Slot]),

  [Entry] = ets:lookup (Table, Id),
  ets:insert (Table, Entry#ssp_camera_slot{allocated=false}),
  ok = write_table (Table, State#state.dir),

  {reply, ok, State};

handle_call ({ssp_camera_slot_is_allocated, Addr, Slot}, _From, State) ->
  Table = State#state.ssp_camera_slots,
  Id = ssp_camera_slot_id (Addr, Slot),

  Result = case ets:lookup (Table, Id) of
    [Entry] when Entry#ssp_camera_slot.allocated ->
      true;
    [] ->
      false end,

  {reply, Result, State};

handle_call ({ssp_camera_slot_allocate, Addr}, _From, State) ->
  Table = State#state.ssp_camera_slots,

  Result = case ets:match_object (Table,
                                  #ssp_camera_slot{id='_', camera_addr=Addr,
                                                   slot='_', allocated=false},
                                  1) of
    {[Entry], _} ->
      error_logger:info_msg ("SSP camera ~p: Allocating slot ~p",
                             [Addr, Entry#ssp_camera_slot.slot]),

      ets:insert (Table, Entry#ssp_camera_slot{allocated=true}),
      ok = write_table (Table, State#state.dir),

      {ok, Entry#ssp_camera_slot.slot};

    '$end_of_table' ->
      {error, no_free_slots} end,

  {reply, Result, State};

handle_call ({ssp_camera_slot_allocate, Addr, Slot}, _From, State) ->
  Table = State#state.ssp_camera_slots,
  Id = ssp_camera_slot_id (Addr, Slot),

  [Entry] = ets:lookup (Table, Id),
  if
    Entry#ssp_camera_slot.allocated ->
      {error, already_allocated};
    true ->
      ets:insert (Table, Entry#ssp_camera_slot{allocated=true}),
      ok = write_table (Table, State#state.dir),
      {reply, ok, State} end.


handle_cast (_Request, State) ->
  {noreply, State}.

handle_info (_Info, State) ->
  {noreply, State}.

code_change (_OldVsn, State, _Extra) ->
  {ok, State}.


%% private

read_table (Name, Dir) ->
  Filename = atom_to_list (Name),

  Path = filename:join (Dir, Filename++".table"),

  error_logger:info_msg ("Loading ~p", [Path]),

  {Exists, List} = case file:consult (Path) of
    {ok, [Data]} ->
      {true, Data};
    {error, enoent} ->
      {false, []} end,

  Table = ets:new (Name, [{keypos, 2}]),
  ets:insert (Table, List),

{'fraction_non_alphanumeric': 0.1347343319174305, 'fraction_numerical': 0.003502496460242939, 'mean_word_length': 3.420289855072464, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6597194', 'n_tokens_mistral': 4748, 'n_tokens_neox': 4572, 'n_words': 1175}