経緯
- VPSのOSをUbuntu 20.04でクリーンインストール
- vsftpsをインストール
- ufwで20, 21ポートを開放
- クライアントからVPSへアクセスするとエラーになる
ufwのステータスは以下。
ソフトウェア開発に関連することを発信しています
AngularでUndo Redo機能を実装する、と題して複数のポストをこちらに投稿していたのだけれど、コード表示が上手に行えなかったのでZenn.devに以下投稿した。
const rayCast = new Ray(origin=new Vector(400, 400), direction=new Vector(-0.8, convertYUp(1)));
const obb = new RotatingRectangle(pos=new Vector(150, 200), size=new Vector(80, 50));
const vectorFromOriginOfRayToCenterOfRect = obb.pos.subtract(rayCast.origin);


const t1 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnXAxis + obb.size.x) / lengthOfDirectionOfRayProjectedOnXAxis; const t2 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnXAxis - obb.size.x) / lengthOfDirectionOfRayProjectedOnXAxis; const t3 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnYAxis + obb.size.y) / lengthOfDirectionOfRayProjectedOnYAxis; const t4 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnYAxis - obb.size.y) / lengthOfDirectionOfRayProjectedOnYAxis;
// TestController.cs
public async Task<TestDto> GetTest(int id, CancellationToken cancellationToken){ // A
var dto = new TestDto();
var data = TestDataFacade.GetDataAsync(id, cancellationToken); // B
dto.Salary = CalculateSalary(); // C
dto.Data = await data; // D
return dto;
}
// TestDataFacade.cs
public async Task<IEnumerable<TestItem>> GetDataAsync(int id, CancellationToken cancellationToken){
using (var con = _connectionProvider.GetEditableConnection()){
await con.OpenAsync(cancellationToken); // A
return await con.QueryAsync<TestItem>( // B
new CommandDefinition( // C
"select * from Tests where id=@id", new { id }, cancellationToken: cancellationToken));
}
}
var currentXHR = $.ajax({
url: "/api/Test/GetTest/1",
type: "GET"
});
currentXHR.done(function (data) {
// 取得したデータで何かする
}).fail(function (jqXHR, textStatus, errorThrown) {
if (errorThrown === "abort") {
alert('処理を中断しました。');
} else {
alert('処理中にエラーが発生しました。エラー内容:' + errorThrown);
}
}).always(function () {
currentXHR = null;
});
// MEMO : 非同期処理中にページ遷移した場合はリクエストをキャンセルする
$(window).unload(function() {
if (currentXHR) {
currentXHR.abort();
}
});
// MEMO : 非同期処理中にキャンセルボタンを押下した場合はリクエストをキャンセルする
$('button.cancel').click(function() {
if (currentXHR) {
currentXHR.abort();
}
});
ページ遷移時に非同期リクエストが処理されている場合は明示的にリクエストをabortしないとキャンセルされない。また重たい処理などを実行する場合はキャンセル処理を実装しておくと不要な処理の実行を防ぐことによってパフォーマンス向上が見込めるので(エンドユーザがレスポンスを待たずにページ遷移してしまったとき用などに)積極的にキャンセル処理は実装したほうが良いだろう。class CancelledTaskBugWorkaroundMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// キャンセルされた場合はエラー内容を空っぽにして送り返す
if (cancellationToken.IsCancellationRequested)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
return response;
}
}
protected void Application_Start(object sender, EventArgs e) {
GlobalConfiguration.Configure(config => {
// ...省略...
// abort処理で発生する例外出力を抑制するための処理
config.MessageHandlers.Add(new CancelledTaskBugWorkaroundMessageHandler());
});
}
[Subject("Test")]
public class TestSpec
{
static TestSpec()
{
// MEMO : ユニットテストでlog4netを使用するための設定
var consoleAppender = new log4net.Appender.ConsoleAppender { Layout = new SimpleLayout() };
BasicConfigurator.Configure(consoleAppender);
}
// 以下省略
}

var arc = d3.svg.arc()
.innerRadius(50)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function (d) { return d.sales; })
.sort(null) // ソートはしない
.startAngle(-Math.PI / 3) // -60度から
.endAngle(Math.PI / 3); // 60度まで
var color = d3.scale.category10();
var container = svg.selectAll('g')
.data(pie(data))
.enter()
.append('g');
// 色をつけて弧を描画する
container.append('path')
.style("fill", function(d, i) { return color(i); })
.attr('d', arc);
// 真ん中に文字を描画する
container.append('text')
.attr('class', 'label value')
.attr('transform', function(d, i) {
return 'translate(' + arc.centroid(d) + ')';
})
.attr('text-anchor', 'middle')
.text(function (d, i) { return d.value; });
// 外側に文字を描画する
container.append('text')
.attr('class', 'label name')
.attr('transform', function(d, i) {
// 弧の外側を取得。パイチャートでは90度(Math.PI/2)の位置が0度計算になっているので注意。それなのでxは-する。yはSVGだと向きが逆になるので+する
var labelR = radius + 20
, x = labelR * Math.cos((d.endAngle - d.startAngle) / 2 + d.startAngle - Math.PI/2)
, y = -labelR * Math.sin((d.endAngle - d.startAngle) / 2 + d.startAngle + Math.PI/2);
return 'translate(' + x + ','+ y + ')';
})
.attr("text-anchor", function(d) {
var angle = (d.endAngle + d.startAngle) / 2;
return 0 < angle && angle < Math.PI ? "start" : "end";
})
.style("fill", function(d, i) { return color(i); })
.text(function(d, i) { return data[i].busho; });

// x軸は日付。y軸にくっつけたくなかったので30からはじめている
var x = d3.time.scale().nice()
.domain(d3.extent(data, function (d) { return d.date; }))
.range([30, width]);
var yearMonthFormat = d3.time.format("%Y/%m");
var xAxis = d3.svg.axis().scale(x)
.orient('bottom')
.tickFormat(yearMonthFormat);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis);
// ツールチップ
var focus = svg.append('g')
.attr('class', 'focus');
focus.append('rect')
.attr({ x: -10, y: -25, width: 105, height: 50 });
// circle
focus.append('circle')
.attr('class', 'profit')
.attr({ r: 5, cy: -15 });
focus.append('circle')
.attr('class', 'sales')
.attr({ r: 5 });
focus.append('circle')
.attr('class', 'expense')
.attr({ r: 5, cy: 15 });
// text
focus.append('text')
.attr('class', 'profit')
.style('text-anchor', 'end')
.attr({ x: 90, y: -15, dy: '.35em' });
focus.append('text')
.attr('class', 'sales')
.style('text-anchor', 'end')
.attr({ x: 90, dy: '.35em' });
focus.append('text')
.attr('class', 'expense')
.style('text-anchor', 'end')
.attr({ x: 90, y: 15, dy: '.35em' });
// オーバーレイ
svg.append('rect')
.attr('class', 'overlay')
.attr({ width: width, height: height })
.on('mouseover', function () { focus.style('display', 'block'); })
.on('mouseout', function () { focus.style('display', 'none'); })
.on('mousemove', mousemove);
var bisectDate = d3.bisector(function (d) { return d.date; }).left
, formatValue = d3.format(",.2f")
, formatCurrency = function (d) { return formatValue(d) + '億円'; };
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0])
, i = bisectDate(data, x0, 1);
if (i < data.length) {
var d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0; // 一番近いデータを取得
focus.attr('transform', 'translate(' + (x(d.date)) + ',' + y(d.sales) + ')');
focus.select('text.profit').text(formatCurrency(d.profit));
focus.select('text.sales').text(formatCurrency(d.sales));
focus.select('text.expense').text(formatCurrency(d.sales - d.profit));
}
}